Merge commit '252d9030cd4b2e3e6cf13fa33f328eccedb5e26c' into HEAD
diff --git a/adf/Android.mk b/adf/Android.mk
new file mode 100644
index 0000000..64d486e
--- /dev/null
+++ b/adf/Android.mk
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2013 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH := $(my-dir)
+
+include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/adf/libadf/Android.mk b/adf/libadf/Android.mk
new file mode 100644
index 0000000..908aa6c
--- /dev/null
+++ b/adf/libadf/Android.mk
@@ -0,0 +1,23 @@
+# Copyright (C) 2013 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := adf.c
+LOCAL_MODULE := libadf
+LOCAL_MODULE_TAGS := optional
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES += $(LOCAL_EXPORT_C_INCLUDE_DIRS)
+include $(BUILD_STATIC_LIBRARY)
diff --git a/adf/libadf/adf.c b/adf/libadf/adf.c
new file mode 100644
index 0000000..871629e
--- /dev/null
+++ b/adf/libadf/adf.c
@@ -0,0 +1,810 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <linux/limits.h>
+
+#include <sys/ioctl.h>
+
+#include <adf/adf.h>
+
+#define ADF_BASE_PATH "/dev/"
+
+static ssize_t adf_find_nodes(const char *pattern, adf_id_t **ids)
+{
+    DIR *dir;
+    struct dirent *dirent;
+    size_t n = 0;
+    ssize_t ret;
+    adf_id_t *ids_ret = NULL;
+
+    dir = opendir(ADF_BASE_PATH);
+    if (!dir)
+        return -errno;
+
+    errno = 0;
+    while ((dirent = readdir(dir))) {
+        adf_id_t id;
+        int matched = sscanf(dirent->d_name, pattern, &id);
+
+        if (matched < 0) {
+            ret = -errno;
+            goto done;
+        } else if (matched != 1) {
+            continue;
+        }
+
+        adf_id_t *new_ids = realloc(ids_ret, (n + 1) * sizeof(ids_ret[0]));
+        if (!new_ids) {
+            ret = -ENOMEM;
+            goto done;
+        }
+
+        ids_ret = new_ids;
+        ids_ret[n] = id;
+        n++;
+    }
+    if (errno)
+        ret = -errno;
+    else
+        ret = n;
+
+done:
+    closedir(dir);
+    if (ret < 0)
+        free(ids_ret);
+    else
+        *ids = ids_ret;
+    return ret;
+}
+
+ssize_t adf_devices(adf_id_t **ids)
+{
+    return adf_find_nodes("adf%u", ids);
+}
+
+int adf_device_open(adf_id_t id, int flags, struct adf_device *dev)
+{
+    char filename[64];
+    int err;
+
+    dev->id = id;
+
+    snprintf(filename, sizeof(filename), ADF_BASE_PATH "adf%u", id);
+    dev->fd = open(filename, flags);
+    if (dev->fd < 0)
+        return -errno;
+
+    return 0;
+}
+
+void adf_device_close(struct adf_device *dev)
+{
+    if (dev->fd >= 0)
+        close(dev->fd);
+}
+
+int adf_get_device_data(struct adf_device *dev, struct adf_device_data *data)
+{
+    int err;
+    int ret = 0;
+
+    memset(data, 0, sizeof(*data));
+
+    err = ioctl(dev->fd, ADF_GET_DEVICE_DATA, data);
+    if (err < 0)
+        return -ENOMEM;
+
+    if (data->n_attachments) {
+        data->attachments = malloc(sizeof(data->attachments[0]) *
+                data->n_attachments);
+        if (!data->attachments)
+            return -ENOMEM;
+    }
+
+    if (data->n_allowed_attachments) {
+        data->allowed_attachments =
+                malloc(sizeof(data->allowed_attachments[0]) *
+                        data->n_allowed_attachments);
+        if (!data->allowed_attachments) {
+            ret = -ENOMEM;
+            goto done;
+        }
+    }
+
+    if (data->custom_data_size) {
+        data->custom_data = malloc(data->custom_data_size);
+        if (!data->custom_data) {
+            ret = -ENOMEM;
+            goto done;
+        }
+    }
+
+    err = ioctl(dev->fd, ADF_GET_DEVICE_DATA, data);
+    if (err < 0)
+        ret = -errno;
+
+done:
+    if (ret < 0)
+        adf_free_device_data(data);
+    return ret;
+}
+
+void adf_free_device_data(struct adf_device_data *data)
+{
+    free(data->attachments);
+    free(data->allowed_attachments);
+    free(data->custom_data);
+}
+
+int adf_device_post(struct adf_device *dev,
+        adf_id_t *interfaces, size_t n_interfaces,
+        struct adf_buffer_config *bufs, size_t n_bufs,
+        void *custom_data, size_t custom_data_size)
+{
+    int err;
+    struct adf_post_config data;
+
+    memset(&data, 0, sizeof(data));
+    data.interfaces = interfaces;
+    data.n_interfaces = n_interfaces;
+    data.bufs = bufs;
+    data.n_bufs = n_bufs;
+    data.custom_data = custom_data;
+    data.custom_data_size = custom_data_size;
+
+    err = ioctl(dev->fd, ADF_POST_CONFIG, &data);
+    if (err < 0)
+        return -errno;
+
+    return (int)data.complete_fence;
+}
+
+static int adf_device_attachment(struct adf_device *dev,
+        adf_id_t overlay_engine, adf_id_t interface, bool attach)
+{
+    int err;
+    struct adf_attachment_config data;
+
+    memset(&data, 0, sizeof(data));
+    data.overlay_engine = overlay_engine;
+    data.interface = interface;
+
+    err = ioctl(dev->fd, attach ? ADF_ATTACH : ADF_DETACH, &data);
+    if (err < 0)
+        return -errno;
+
+    return 0;
+}
+
+int adf_device_attach(struct adf_device *dev, adf_id_t overlay_engine,
+                      adf_id_t interface)
+{
+   return adf_device_attachment(dev, overlay_engine, interface, true);
+}
+
+int adf_device_detach(struct adf_device *dev, adf_id_t overlay_engine,
+                      adf_id_t interface)
+{
+   return adf_device_attachment(dev, overlay_engine, interface, false);
+}
+
+ssize_t adf_interfaces(struct adf_device *dev, adf_id_t **interfaces)
+{
+    char pattern[64];
+
+    snprintf(pattern, sizeof(pattern), "adf-interface%u.%%u", dev->id);
+    return adf_find_nodes(pattern, interfaces);
+}
+
+ssize_t adf_interfaces_for_overlay_engine(struct adf_device *dev,
+        adf_id_t overlay_engine, adf_id_t **interfaces)
+{
+    struct adf_device_data data;
+    ssize_t n = 0;
+    ssize_t ret;
+    adf_id_t *ids_ret = NULL;
+
+    ret = adf_get_device_data(dev, &data);
+    if (ret < 0)
+        return ret;
+
+    size_t i;
+    for (i = 0; i < data.n_allowed_attachments; i++) {
+        if (data.allowed_attachments[i].overlay_engine != overlay_engine)
+            continue;
+
+        adf_id_t *new_ids = realloc(ids_ret, (n + 1) * sizeof(ids_ret[0]));
+        if (!new_ids) {
+            ret = -ENOMEM;
+            goto done;
+        }
+
+        ids_ret = new_ids;
+        ids_ret[n] = data.allowed_attachments[i].interface;
+        n++;
+    }
+
+    ret = n;
+
+done:
+    adf_free_device_data(&data);
+    if (ret < 0)
+        free(ids_ret);
+    else
+        *interfaces = ids_ret;
+    return ret;
+}
+
+static ssize_t adf_interfaces_filter(struct adf_device *dev,
+        adf_id_t *in, size_t n_in, adf_id_t **out,
+        bool (*filter)(struct adf_interface_data *data, __u32 match),
+        __u32 match)
+{
+    size_t n = 0;
+    ssize_t ret;
+    adf_id_t *ids_ret = NULL;
+
+    size_t i;
+    for (i = 0; i < n_in; i++) {
+        int fd = adf_interface_open(dev, in[i], O_RDONLY);
+        if (fd < 0) {
+            ret = fd;
+            goto done;
+        }
+
+        struct adf_interface_data data;
+        ret = adf_get_interface_data(fd, &data);
+        close(fd);
+        if (ret < 0)
+            goto done;
+
+        if (!filter(&data, match))
+            continue;
+
+        adf_id_t *new_ids = realloc(ids_ret, (n + 1) * sizeof(ids_ret[0]));
+        if (!new_ids) {
+            ret = -ENOMEM;
+            goto done;
+        }
+
+        ids_ret = new_ids;
+        ids_ret[n] = in[i];
+        n++;
+    }
+
+    ret = n;
+
+done:
+    if (ret < 0)
+        free(ids_ret);
+    else
+        *out = ids_ret;
+    return ret;
+}
+
+static bool adf_interface_type_filter(struct adf_interface_data *data,
+        __u32 type)
+{
+    return data->type == (enum adf_interface_type)type;
+}
+
+ssize_t adf_interfaces_filter_by_type(struct adf_device *dev,
+        enum adf_interface_type type,
+        adf_id_t *in, size_t n_in, adf_id_t **out)
+{
+    return adf_interfaces_filter(dev, in, n_in, out, adf_interface_type_filter,
+            type);
+}
+
+static bool adf_interface_flags_filter(struct adf_interface_data *data,
+        __u32 flag)
+{
+    return !!(data->flags & flag);
+}
+
+ssize_t adf_interfaces_filter_by_flag(struct adf_device *dev, __u32 flag,
+        adf_id_t *in, size_t n_in, adf_id_t **out)
+{
+    return adf_interfaces_filter(dev, in, n_in, out, adf_interface_flags_filter,
+            flag);
+}
+
+int adf_interface_open(struct adf_device *dev, adf_id_t id, int flags)
+{
+    char filename[64];
+
+    snprintf(filename, sizeof(filename), ADF_BASE_PATH "adf-interface%u.%u",
+            dev->id, id);
+
+    int fd = open(filename, flags);
+    if (fd < 0)
+        return -errno;
+    return fd;
+}
+
+int adf_get_interface_data(int fd, struct adf_interface_data *data)
+{
+    int err;
+    int ret = 0;
+
+    memset(data, 0, sizeof(*data));
+
+    err = ioctl(fd, ADF_GET_INTERFACE_DATA, data);
+    if (err < 0)
+        return -errno;
+
+    if (data->n_available_modes) {
+        data->available_modes = malloc(sizeof(data->available_modes[0]) *
+                data->n_available_modes);
+        if (!data->available_modes)
+            return -ENOMEM;
+    }
+
+    if (data->custom_data_size) {
+        data->custom_data = malloc(data->custom_data_size);
+        if (!data->custom_data) {
+            ret = -ENOMEM;
+            goto done;
+        }
+    }
+
+    err = ioctl(fd, ADF_GET_INTERFACE_DATA, data);
+    if (err < 0)
+        ret = -errno;
+
+done:
+    if (ret < 0)
+        adf_free_interface_data(data);
+    return ret;
+}
+
+void adf_free_interface_data(struct adf_interface_data *data)
+{
+    free(data->available_modes);
+    free(data->custom_data);
+}
+
+int adf_interface_blank(int fd, __u8 mode)
+{
+    int err = ioctl(fd, ADF_BLANK, mode);
+    if (err < 0)
+        return -errno;
+    return 0;
+}
+
+int adf_interface_set_mode(int fd, struct drm_mode_modeinfo *mode)
+{
+    int err = ioctl(fd, ADF_SET_MODE, mode);
+    if (err < 0)
+        return -errno;
+    return 0;
+}
+
+int adf_interface_simple_buffer_alloc(int fd, __u32 w, __u32 h,
+        __u32 format, __u32 *offset, __u32 *pitch)
+{
+    int err;
+    struct adf_simple_buffer_alloc data;
+
+    memset(&data, 0, sizeof(data));
+    data.w = w;
+    data.h = h;
+    data.format = format;
+
+    err = ioctl(fd, ADF_SIMPLE_BUFFER_ALLOC, &data);
+    if (err < 0)
+        return -errno;
+
+    *offset = data.offset;
+    *pitch = data.pitch;
+    return (int)data.fd;
+}
+
+int adf_interface_simple_post(int fd, __u32 overlay_engine,
+        __u32 w, __u32 h, __u32 format, int buf_fd, __u32 offset,
+        __u32 pitch, int acquire_fence)
+{
+    int ret;
+    struct adf_simple_post_config data;
+
+    memset(&data, 0, sizeof(data));
+    data.buf.overlay_engine = overlay_engine;
+    data.buf.w = w;
+    data.buf.h = h;
+    data.buf.format = format;
+    data.buf.fd[0] = buf_fd;
+    data.buf.offset[0] = offset;
+    data.buf.pitch[0] = pitch;
+    data.buf.n_planes = 1;
+    data.buf.acquire_fence = acquire_fence;
+
+    ret = ioctl(fd, ADF_SIMPLE_POST_CONFIG, &data);
+    if (ret < 0)
+        return -errno;
+
+    return (int)data.complete_fence;
+}
+
+ssize_t adf_overlay_engines(struct adf_device *dev, adf_id_t **overlay_engines)
+{
+    char pattern[64];
+
+    snprintf(pattern, sizeof(pattern), "adf-overlay-engine%u.%%u", dev->id);
+    return adf_find_nodes(pattern, overlay_engines);
+}
+
+ssize_t adf_overlay_engines_for_interface(struct adf_device *dev,
+        adf_id_t interface, adf_id_t **overlay_engines)
+{
+    struct adf_device_data data;
+    ssize_t n = 0;
+    ssize_t ret;
+    adf_id_t *ids_ret = NULL;
+
+    ret = adf_get_device_data(dev, &data);
+    if (ret < 0)
+        return ret;
+
+    size_t i;
+    for (i = 0; i < data.n_allowed_attachments; i++) {
+        if (data.allowed_attachments[i].interface != interface)
+            continue;
+
+        adf_id_t *new_ids = realloc(ids_ret, (n + 1) * sizeof(ids_ret[0]));
+        if (!new_ids) {
+            ret = -ENOMEM;
+            goto done;
+        }
+
+        ids_ret = new_ids;
+        ids_ret[n] = data.allowed_attachments[i].overlay_engine;
+        n++;
+    }
+
+    ret = n;
+
+done:
+    adf_free_device_data(&data);
+    if (ret < 0)
+        free(ids_ret);
+    else
+        *overlay_engines = ids_ret;
+    return ret;
+}
+
+static ssize_t adf_overlay_engines_filter(struct adf_device *dev,
+        adf_id_t *in, size_t n_in, adf_id_t **out,
+        bool (*filter)(struct adf_overlay_engine_data *data, void *cookie),
+        void *cookie)
+{
+    size_t n = 0;
+    ssize_t ret;
+    adf_id_t *ids_ret = NULL;
+
+    size_t i;
+    for (i = 0; i < n_in; i++) {
+        int fd = adf_overlay_engine_open(dev, in[i], O_RDONLY);
+        if (fd < 0) {
+            ret = fd;
+            goto done;
+        }
+
+        struct adf_overlay_engine_data data;
+        ret = adf_get_overlay_engine_data(fd, &data);
+        close(fd);
+        if (ret < 0)
+            goto done;
+
+        if (!filter(&data, cookie))
+            continue;
+
+        adf_id_t *new_ids = realloc(ids_ret, (n + 1) * sizeof(ids_ret[0]));
+        if (!new_ids) {
+            ret = -ENOMEM;
+            goto done;
+        }
+
+        ids_ret = new_ids;
+        ids_ret[n] = in[i];
+        n++;
+    }
+
+    ret = n;
+
+done:
+    if (ret < 0)
+        free(ids_ret);
+    else
+        *out = ids_ret;
+    return ret;
+}
+
+struct format_filter_cookie {
+    const __u32 *formats;
+    size_t n_formats;
+};
+
+static bool adf_overlay_engine_format_filter(
+        struct adf_overlay_engine_data *data, void *cookie)
+{
+    struct format_filter_cookie *c = cookie;
+    size_t i;
+    for (i = 0; i < data->n_supported_formats; i++) {
+        size_t j;
+        for (j = 0; j < c->n_formats; j++)
+            if (data->supported_formats[i] == c->formats[j])
+                return true;
+    }
+    return false;
+}
+
+ssize_t adf_overlay_engines_filter_by_format(struct adf_device *dev,
+        const __u32 *formats, size_t n_formats, adf_id_t *in, size_t n_in,
+        adf_id_t **out)
+{
+    struct format_filter_cookie cookie = { formats, n_formats };
+    return adf_overlay_engines_filter(dev, in, n_in, out,
+            adf_overlay_engine_format_filter, &cookie);
+}
+
+int adf_overlay_engine_open(struct adf_device *dev, adf_id_t id, int flags)
+{
+    char filename[64];
+
+    snprintf(filename, sizeof(filename),
+            ADF_BASE_PATH "adf-overlay-engine%u.%u", dev->id, id);
+
+    int fd = open(filename, flags);
+    if (fd < 0)
+        return -errno;
+    return fd;
+}
+
+int adf_get_overlay_engine_data(int fd, struct adf_overlay_engine_data *data)
+{
+    int err;
+    int ret = 0;
+
+    memset(data, 0, sizeof(*data));
+
+    err = ioctl(fd, ADF_GET_OVERLAY_ENGINE_DATA, data);
+    if (err < 0)
+        return -errno;
+
+    if (data->n_supported_formats) {
+        data->supported_formats = malloc(sizeof(data->supported_formats[0]) *
+              data->n_supported_formats);
+        if (!data->supported_formats)
+            return -ENOMEM;
+    }
+
+    if (data->custom_data_size) {
+      data->custom_data = malloc(data->custom_data_size);
+      if (!data->custom_data) {
+          ret = -ENOMEM;
+          goto done;
+      }
+    }
+
+    err = ioctl(fd, ADF_GET_OVERLAY_ENGINE_DATA, data);
+    if (err < 0)
+        ret = -errno;
+
+done:
+    if (ret < 0)
+        adf_free_overlay_engine_data(data);
+    return ret;
+}
+
+void adf_free_overlay_engine_data(struct adf_overlay_engine_data *data)
+{
+    free(data->supported_formats);
+    free(data->custom_data);
+}
+
+bool adf_overlay_engine_supports_format(int fd, __u32 format)
+{
+    struct adf_overlay_engine_data data;
+    bool ret = false;
+    size_t i;
+
+    int err = adf_get_overlay_engine_data(fd, &data);
+    if (err < 0)
+        return false;
+
+    for (i = 0; i < data.n_supported_formats; i++) {
+        if (data.supported_formats[i] == format) {
+            ret = true;
+            break;
+        }
+    }
+
+    adf_free_overlay_engine_data(&data);
+    return ret;
+}
+
+int adf_set_event(int fd, enum adf_event_type type, bool enabled)
+{
+    struct adf_set_event data;
+
+    data.type = type;
+    data.enabled = enabled;
+
+    int err = ioctl(fd, ADF_SET_EVENT, &data);
+    if (err < 0)
+        return -errno;
+    return 0;
+}
+
+int adf_read_event(int fd, struct adf_event **event)
+{
+    struct adf_event header;
+    struct {
+        struct adf_event base;
+        uint8_t data[0];
+    } *event_ret;
+    size_t data_size;
+    int ret = 0;
+
+    int err = read(fd, &header, sizeof(header));
+    if (err < 0)
+        return -errno;
+    if ((size_t)err < sizeof(header))
+        return -EIO;
+    if (header.length < sizeof(header))
+        return -EIO;
+
+    event_ret = malloc(header.length);
+    if (!event_ret)
+        return -ENOMEM;
+    data_size = header.length - sizeof(header);
+
+    memcpy(event_ret, &header, sizeof(header));
+    ssize_t read_size = read(fd, &event_ret->data, data_size);
+    if (read_size < 0) {
+        ret = -errno;
+        goto done;
+    }
+    if ((size_t)read_size < data_size) {
+        ret = -EIO;
+        goto done;
+    }
+
+    *event = &event_ret->base;
+
+done:
+    if (ret < 0)
+        free(event_ret);
+    return ret;
+}
+
+void adf_format_str(__u32 format, char buf[ADF_FORMAT_STR_SIZE])
+{
+    buf[0] = format & 0xFF;
+    buf[1] = (format >> 8) & 0xFF;
+    buf[2] = (format >> 16) & 0xFF;
+    buf[3] = (format >> 24) & 0xFF;
+    buf[4] = '\0';
+}
+
+static bool adf_find_simple_post_overlay_engine(struct adf_device *dev,
+        const __u32 *formats, size_t n_formats,
+        adf_id_t interface, adf_id_t *overlay_engine)
+{
+    adf_id_t *engs;
+    ssize_t n_engs = adf_overlay_engines_for_interface(dev, interface, &engs);
+
+    if (n_engs <= 0)
+        return false;
+
+    adf_id_t *filtered_engs;
+    ssize_t n_filtered_engs = adf_overlay_engines_filter_by_format(dev,
+            formats, n_formats, engs, n_engs, &filtered_engs);
+    free(engs);
+
+    if (n_filtered_engs <= 0)
+        return false;
+
+    *overlay_engine = filtered_engs[0];
+    free(filtered_engs);
+    return true;
+}
+
+static const __u32 any_rgb_format[] = {
+    DRM_FORMAT_C8,
+    DRM_FORMAT_RGB332,
+    DRM_FORMAT_BGR233,
+    DRM_FORMAT_XRGB1555,
+    DRM_FORMAT_XBGR1555,
+    DRM_FORMAT_RGBX5551,
+    DRM_FORMAT_BGRX5551,
+    DRM_FORMAT_ARGB1555,
+    DRM_FORMAT_ABGR1555,
+    DRM_FORMAT_RGBA5551,
+    DRM_FORMAT_BGRA5551,
+    DRM_FORMAT_RGB565,
+    DRM_FORMAT_BGR565,
+    DRM_FORMAT_RGB888,
+    DRM_FORMAT_BGR888,
+    DRM_FORMAT_XRGB8888,
+    DRM_FORMAT_XBGR8888,
+    DRM_FORMAT_RGBX8888,
+    DRM_FORMAT_BGRX8888,
+    DRM_FORMAT_XRGB2101010,
+    DRM_FORMAT_XBGR2101010,
+    DRM_FORMAT_RGBX1010102,
+    DRM_FORMAT_BGRX1010102,
+    DRM_FORMAT_ARGB2101010,
+    DRM_FORMAT_ABGR2101010,
+    DRM_FORMAT_RGBA1010102,
+    DRM_FORMAT_BGRA1010102,
+    DRM_FORMAT_ARGB8888,
+    DRM_FORMAT_ABGR8888,
+    DRM_FORMAT_RGBA8888,
+    DRM_FORMAT_BGRA8888,
+};
+
+int adf_find_simple_post_configuration(struct adf_device *dev,
+        const __u32 *formats, size_t n_formats,
+        adf_id_t *interface, adf_id_t *overlay_engine)
+{
+    adf_id_t *intfs;
+    ssize_t n_intfs = adf_interfaces(dev, &intfs);
+
+    if (n_intfs < 0)
+        return n_intfs;
+    else if (!n_intfs)
+        return -ENODEV;
+
+    adf_id_t *primary_intfs;
+    ssize_t n_primary_intfs = adf_interfaces_filter_by_flag(dev,
+            ADF_INTF_FLAG_PRIMARY, intfs, n_intfs, &primary_intfs);
+    free(intfs);
+
+    if (n_primary_intfs < 0)
+        return n_primary_intfs;
+    else if (!n_primary_intfs)
+        return -ENODEV;
+
+    if (!formats) {
+        formats = any_rgb_format;
+        n_formats = sizeof(any_rgb_format) / sizeof(any_rgb_format[0]);
+    }
+
+    bool found = false;
+    ssize_t i = 0;
+    for (i = 0; i < n_primary_intfs; i++) {
+        found = adf_find_simple_post_overlay_engine(dev, formats, n_formats,
+                primary_intfs[i], overlay_engine);
+        if (found) {
+            *interface = primary_intfs[i];
+            break;
+        }
+    }
+    free(primary_intfs);
+
+    if (!found)
+        return -ENODEV;
+
+    return 0;
+}
diff --git a/adf/libadf/include/adf/adf.h b/adf/libadf/include/adf/adf.h
new file mode 100644
index 0000000..b6bda34
--- /dev/null
+++ b/adf/libadf/include/adf/adf.h
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBADF_ADF_H_
+#define _LIBADF_ADF_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <video/adf.h>
+
+typedef __u32 adf_id_t;
+
+struct adf_device {
+    adf_id_t id;
+    int fd;
+};
+
+__BEGIN_DECLS
+
+/**
+ * Enumerates all ADF devices.
+ *
+ * Returns the number of ADF devices, and sets ids to a list of device IDs.
+ * The caller must free() the returned list of device IDs.
+ *
+ * On error, returns -errno.
+ */
+ssize_t adf_devices(adf_id_t **ids);
+
+/**
+ * Opens an ADF device.
+ *
+ * On error, returns -errno.
+ */
+int adf_device_open(adf_id_t id, int flags, struct adf_device *dev);
+/**
+ * Closes an ADF device.
+ */
+void adf_device_close(struct adf_device *dev);
+/**
+ * Reads the ADF device data.
+ *
+ * adf_get_device_data() allocates buffers inside data, which the caller
+ * must free by calling adf_free_device_data().  On error, returns -errno.
+ */
+int adf_get_device_data(struct adf_device *dev, struct adf_device_data *data);
+/**
+ * Frees the device data returned by adf_get_device_data().
+ */
+void adf_free_device_data(struct adf_device_data *data);
+
+/**
+ * Atomically posts a new display configuration to the specified interfaces.
+ *
+ * Returns a sync fence fd that will fire when the configuration is removed
+ * from the screen.  On error, returns -errno.
+ */
+int adf_device_post(struct adf_device *dev,
+        adf_id_t *interfaces, size_t n_interfaces,
+        struct adf_buffer_config *bufs, size_t n_bufs,
+        void *custom_data, size_t custom_data_size);
+/**
+ * Attaches the specified interface and overlay engine.
+ */
+int adf_device_attach(struct adf_device *dev, adf_id_t overlay_engine,
+                      adf_id_t interface);
+/**
+ * Detaches the specified interface and overlay engine.
+ */
+int adf_device_detach(struct adf_device *dev, adf_id_t overlay_engine,
+                      adf_id_t interface);
+
+/**
+ * Enumerates all interfaces belonging to an ADF device.
+ *
+ * The caller must free() the returned list of interface IDs.
+ */
+ssize_t adf_interfaces(struct adf_device *dev, adf_id_t **interfaces);
+
+/**
+ * Enumerates all interfaces which can be attached to the specified overlay
+ * engine.
+ *
+ * The caller must free() the returned list of interface IDs.
+ */
+ssize_t adf_interfaces_for_overlay_engine(struct adf_device *dev,
+        adf_id_t overlay_engine, adf_id_t **interfaces);
+/**
+ * Filters a list of interfaces by type.
+ *
+ * Returns the number of matching interfaces, and sets out to a list of matching
+ * interface IDs.  The caller must free() the returned list of interface IDs.
+ *
+ * On error, returns -errno.
+ */
+ssize_t adf_interfaces_filter_by_type(struct adf_device *dev,
+        enum adf_interface_type type,
+        adf_id_t *in, size_t n_in, adf_id_t **out);
+/**
+ * Filters a list of interfaces by flag.
+ *
+ * The caller must free() the returned list of interface IDs.
+ */
+ssize_t adf_interfaces_filter_by_flag(struct adf_device *dev, __u32 flag,
+        adf_id_t *in, size_t n_in, adf_id_t **out);
+
+/**
+ * Opens an ADF interface.
+ *
+ * Returns a file descriptor.  The caller must close() the fd when done.
+ * On error, returns -errno.
+ */
+int adf_interface_open(struct adf_device *dev, adf_id_t id, int flags);
+/**
+ * Reads the interface data.
+ *
+ * adf_get_interface_data() allocates buffers inside data, which the caller
+ * must free by calling adf_free_interface_data().  On error, returns -errno.
+ */
+int adf_get_interface_data(int fd, struct adf_interface_data *data);
+/**
+ * Frees the interface data returned by adf_get_interface_data().
+ */
+void adf_free_interface_data(struct adf_interface_data *data);
+
+/**
+ * Sets the interface's DPMS mode.
+ */
+int adf_interface_blank(int fd, __u8 mode);
+/**
+ * Sets the interface's display mode.
+ */
+int adf_interface_set_mode(int fd, struct drm_mode_modeinfo *mode);
+/**
+ * Allocates a single-plane RGB buffer of the specified size and format.
+ *
+ * Returns a dma-buf fd.  On error, returns -errno.
+ */
+int adf_interface_simple_buffer_alloc(int fd, __u32 w, __u32 h,
+        __u32 format, __u32 *offset, __u32 *pitch);
+/**
+ * Posts a single-plane RGB buffer to the display using the specified
+ * overlay engine.
+ *
+ * Returns a sync fence fd that will fire when the buffer is removed
+ * from the screen.  On error, returns -errno.
+ */
+int adf_interface_simple_post(int fd, adf_id_t overlay_engine,
+        __u32 w, __u32 h, __u32 format, int buf_fd, __u32 offset,
+        __u32 pitch, int acquire_fence);
+
+/**
+ * Enumerates all overlay engines belonging to an ADF device.
+ *
+ * The caller must free() the returned list of overlay engine IDs.
+ */
+ssize_t adf_overlay_engines(struct adf_device *dev, adf_id_t **overlay_engines);
+
+/**
+ * Enumerates all overlay engines which can be attached to the specified
+ * interface.
+ *
+ * The caller must free() the returned list of overlay engine IDs.
+ */
+ssize_t adf_overlay_engines_for_interface(struct adf_device *dev,
+        adf_id_t interface, adf_id_t **overlay_engines);
+/**
+ * Filters a list of overlay engines by supported buffer format.
+ *
+ * Returns the overlay engines which support at least one of the specified
+ * formats.  The caller must free() the returned list of overlay engine IDs.
+ */
+ssize_t adf_overlay_engines_filter_by_format(struct adf_device *dev,
+        const __u32 *formats, size_t n_formats, adf_id_t *in, size_t n_in,
+        adf_id_t **out);
+
+/**
+ * Opens an ADF overlay engine.
+ *
+ * Returns a file descriptor.  The caller must close() the fd when done.
+ * On error, returns -errno.
+ */
+int adf_overlay_engine_open(struct adf_device *dev, adf_id_t id, int flags);
+/**
+ * Reads the overlay engine data.
+ *
+ * adf_get_overlay_engine_data() allocates buffers inside data, which the caller
+ * must free by calling adf_free_overlay_engine_data().  On error, returns
+ * -errno.
+ */
+int adf_get_overlay_engine_data(int fd, struct adf_overlay_engine_data *data);
+/**
+ * Frees the overlay engine data returned by adf_get_overlay_engine_data().
+ */
+void adf_free_overlay_engine_data(struct adf_overlay_engine_data *data);
+
+/**
+ * Returns whether the overlay engine supports the specified format.
+ */
+bool adf_overlay_engine_supports_format(int fd, __u32 format);
+
+/**
+ * Subscribes or unsubscribes from the specified hardware event.
+ */
+int adf_set_event(int fd, enum adf_event_type type, bool enabled);
+/**
+ * Reads one event from the fd, blocking if needed.
+ *
+ * The caller must free() the returned buffer.  On error, returns -errno.
+ */
+int adf_read_event(int fd, struct adf_event **event);
+
+#define ADF_FORMAT_STR_SIZE 5
+/**
+ * Converts an ADF/DRM fourcc format to its string representation.
+ */
+void adf_format_str(__u32 format, char buf[ADF_FORMAT_STR_SIZE]);
+
+/**
+ * Finds an appropriate interface and overlay engine for a simple post.
+ *
+ * Specifically, finds the primary interface, and an overlay engine
+ * that can be attached to the primary interface and supports one of the
+ * specified formats.  The caller may pass a NULL formats list, to indicate that
+ * any RGB format is acceptable.
+ *
+ * On error, returns -errno.
+ */
+int adf_find_simple_post_configuration(struct adf_device *dev,
+        const __u32 *formats, size_t n_formats,
+        adf_id_t *interface, adf_id_t *overlay_engine);
+
+__END_DECLS
+
+#endif /* _LIBADF_ADF_H_ */
diff --git a/adf/libadfhwc/Android.mk b/adf/libadfhwc/Android.mk
new file mode 100644
index 0000000..acea322
--- /dev/null
+++ b/adf/libadfhwc/Android.mk
@@ -0,0 +1,25 @@
+# Copyright (C) 2013 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := adfhwc.cpp
+LOCAL_MODULE := libadfhwc
+LOCAL_MODULE_TAGS := optional
+LOCAL_STATIC_LIBRARIES := libadf liblog libutils
+LOCAL_CFLAGS += -DLOG_TAG=\"adfhwc\"
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES += $(LOCAL_EXPORT_C_INCLUDE_DIRS)
+include $(BUILD_STATIC_LIBRARY)
diff --git a/adf/libadfhwc/adfhwc.cpp b/adf/libadfhwc/adfhwc.cpp
new file mode 100644
index 0000000..dee3cae
--- /dev/null
+++ b/adf/libadfhwc/adfhwc.cpp
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <poll.h>
+#include <pthread.h>
+#include <sys/resource.h>
+
+#include <adf/adf.h>
+#include <adfhwc/adfhwc.h>
+
+#include <cutils/log.h>
+#include <utils/Vector.h>
+
+struct adf_hwc_helper {
+    adf_hwc_event_callbacks const *event_cb;
+    void *event_cb_data;
+
+    pthread_t event_thread;
+
+    android::Vector<int> intf_fds;
+    android::Vector<drm_mode_modeinfo> display_configs;
+};
+
+template<typename T> inline T min(T a, T b) { return (a < b) ? a : b; }
+
+int adf_eventControl(struct adf_hwc_helper *dev, int disp, int event,
+        int enabled)
+{
+    if (enabled != !!enabled)
+        return -EINVAL;
+
+    if ((size_t)disp >= dev->intf_fds.size())
+        return -EINVAL;
+
+    switch (event) {
+    case HWC_EVENT_VSYNC:
+        return adf_set_event(dev->intf_fds[disp], ADF_EVENT_VSYNC, enabled);
+    }
+
+    return -EINVAL;
+}
+
+static inline int32_t dpi(uint16_t res, uint16_t size_mm)
+{
+    if (size_mm)
+        return 1000 * (res * 25.4f) / size_mm;
+    return 0;
+}
+
+int adf_blank(struct adf_hwc_helper *dev, int disp, int blank)
+{
+    if ((size_t)disp >= dev->intf_fds.size())
+        return -EINVAL;
+
+    uint8_t dpms_mode = blank ? DRM_MODE_DPMS_OFF : DRM_MODE_DPMS_ON;
+    return adf_interface_blank(dev->intf_fds[disp], dpms_mode);
+}
+
+int adf_query_display_types_supported(struct adf_hwc_helper *dev, int *value)
+{
+    *value = 0;
+    if (dev->intf_fds.size() > 0)
+        *value |= HWC_DISPLAY_PRIMARY_BIT;
+    if (dev->intf_fds.size() > 1)
+        *value |= HWC_DISPLAY_EXTERNAL_BIT;
+
+    return 0;
+}
+
+int adf_getDisplayConfigs(struct adf_hwc_helper *dev, int disp,
+        uint32_t *configs, size_t *numConfigs)
+{
+    if ((size_t)disp >= dev->intf_fds.size())
+        return -EINVAL;
+
+    adf_interface_data data;
+    int err = adf_get_interface_data(dev->intf_fds[disp], &data);
+    if (err < 0) {
+        ALOGE("failed to get ADF interface data: %s", strerror(err));
+        return err;
+    }
+
+    if (!data.hotplug_detect)
+        return -ENODEV;
+
+    android::Vector<drm_mode_modeinfo *> unique_configs;
+    unique_configs.push_back(&data.current_mode);
+    for (size_t i = 0; i < data.n_available_modes; i++)
+        if (memcmp(&data.available_modes[i], &data.current_mode,
+                sizeof(data.current_mode)))
+            unique_configs.push_back(&data.available_modes[i]);
+
+    for (size_t i = 0; i < min(*numConfigs, unique_configs.size()); i++) {
+        configs[i] = dev->display_configs.size();
+        dev->display_configs.push_back(*unique_configs[i]);
+    }
+    *numConfigs = unique_configs.size();
+
+    adf_free_interface_data(&data);
+    return 0;
+}
+
+static int32_t adf_display_attribute(const adf_interface_data &data,
+        const drm_mode_modeinfo &mode, const uint32_t attribute)
+{
+    switch (attribute) {
+    case HWC_DISPLAY_VSYNC_PERIOD:
+        if (mode.vrefresh)
+            return 1000000000 / mode.vrefresh;
+        return 0;
+
+    case HWC_DISPLAY_WIDTH:
+        return mode.hdisplay;
+
+    case HWC_DISPLAY_HEIGHT:
+        return mode.vdisplay;
+
+    case HWC_DISPLAY_DPI_X:
+        return dpi(mode.hdisplay, data.width_mm);
+
+    case HWC_DISPLAY_DPI_Y:
+        return dpi(mode.vdisplay, data.height_mm);
+
+    default:
+        ALOGE("unknown display attribute %u", attribute);
+        return -EINVAL;
+    }
+}
+
+int adf_getDisplayAttributes(struct adf_hwc_helper *dev, int disp,
+        uint32_t config, const uint32_t *attributes, int32_t *values)
+{
+    if ((size_t)disp >= dev->intf_fds.size())
+        return -EINVAL;
+
+    if (config >= dev->display_configs.size())
+        return -EINVAL;
+
+    adf_interface_data data;
+    int err = adf_get_interface_data(dev->intf_fds[disp], &data);
+    if (err < 0) {
+        ALOGE("failed to get ADF interface data: %s", strerror(err));
+        return err;
+    }
+
+    for (int i = 0; attributes[i] != HWC_DISPLAY_NO_ATTRIBUTE; i++)
+        values[i] = adf_display_attribute(data, dev->display_configs[config],
+                attributes[i]);
+
+    adf_free_interface_data(&data);
+    return 0;
+}
+
+static void handle_adf_event(struct adf_hwc_helper *dev, int disp)
+{
+    adf_event *event;
+    int err = adf_read_event(dev->intf_fds[disp], &event);
+    if (err < 0) {
+        ALOGE("error reading event from display %d: %s", disp, strerror(err));
+        return;
+    }
+
+    void *vsync_temp;
+    adf_vsync_event *vsync;
+    adf_hotplug_event *hotplug;
+
+    switch (event->type) {
+    case ADF_EVENT_VSYNC:
+        vsync_temp = event;
+        vsync = static_cast<adf_vsync_event *>(vsync_temp);
+        // casting directly to adf_vsync_event * makes g++ warn about
+        // potential alignment issues that don't apply here
+        dev->event_cb->vsync(dev->event_cb_data, disp, vsync->timestamp);
+        break;
+    case ADF_EVENT_HOTPLUG:
+        hotplug = reinterpret_cast<adf_hotplug_event *>(event);
+        dev->event_cb->hotplug(dev->event_cb_data, disp, hotplug->connected);
+        break;
+    default:
+        if (event->type < ADF_EVENT_DEVICE_CUSTOM)
+            ALOGW("unrecognized event type %u", event->type);
+        else if (!dev->event_cb || !dev->event_cb->custom_event)
+            ALOGW("unhandled event type %u", event->type);
+        else
+            dev->event_cb->custom_event(dev->event_cb_data, disp, event);
+    }
+    free(event);
+}
+
+static void *adf_event_thread(void *data)
+{
+    adf_hwc_helper *dev = static_cast<adf_hwc_helper *>(data);
+
+    setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY);
+
+    pollfd *fds = new pollfd[dev->intf_fds.size()];
+    for (size_t i = 0; i < dev->intf_fds.size(); i++) {
+        fds[i].fd = dev->intf_fds[i];
+        fds[i].events = POLLIN | POLLPRI;
+    }
+
+    while (true) {
+        int err = poll(fds, dev->intf_fds.size(), -1);
+
+        if (err > 0) {
+            for (size_t i = 0; i < dev->intf_fds.size(); i++)
+                if (fds[i].revents & (POLLIN | POLLPRI))
+                    handle_adf_event(dev, i);
+        }
+        else if (err == -1) {
+            if (errno == EINTR)
+                break;
+            ALOGE("error in event thread: %s", strerror(errno));
+        }
+    }
+
+    delete [] fds;
+    return NULL;
+}
+
+int adf_hwc_open(int *intf_fds, size_t n_intfs,
+        const struct adf_hwc_event_callbacks *event_cb, void *event_cb_data,
+        struct adf_hwc_helper **dev)
+{
+    if (!n_intfs)
+        return -EINVAL;
+
+    adf_hwc_helper *dev_ret = new adf_hwc_helper;
+    dev_ret->event_cb = event_cb;
+    dev_ret->event_cb_data = event_cb_data;
+
+    int ret;
+
+    for (size_t i = 0; i < n_intfs; i++) {
+        int dup_intf_fd = dup(intf_fds[i]);
+        if (dup_intf_fd < 0) {
+            ALOGE("failed to dup interface fd: %s", strerror(errno));
+            ret = -errno;
+            goto err;
+        }
+
+        dev_ret->intf_fds.push_back(dup_intf_fd);
+
+        ret = adf_set_event(dup_intf_fd, ADF_EVENT_HOTPLUG, 1);
+        if (ret < 0 && ret != -EINVAL) {
+            ALOGE("failed to enable hotplug event on display %u: %s",
+                    i, strerror(errno));
+            goto err;
+        }
+    }
+
+    ret = pthread_create(&dev_ret->event_thread, NULL, adf_event_thread,
+            dev_ret);
+    if (ret) {
+        ALOGE("failed to create event thread: %s", strerror(ret));
+        goto err;
+    }
+
+    *dev = dev_ret;
+    return 0;
+
+err:
+    for (size_t i = 0; i < dev_ret->intf_fds.size(); i++)
+        close(dev_ret->intf_fds[i]);
+
+    delete dev_ret;
+    return ret;
+}
+
+void adf_hwc_close(struct adf_hwc_helper *dev)
+{
+    pthread_kill(dev->event_thread, SIGTERM);
+    pthread_join(dev->event_thread, NULL);
+
+    for (size_t i = 0; i < dev->intf_fds.size(); i++)
+        close(dev->intf_fds[i]);
+
+    delete dev;
+}
diff --git a/adf/libadfhwc/include/adfhwc/adfhwc.h b/adf/libadfhwc/include/adfhwc/adfhwc.h
new file mode 100644
index 0000000..71e7624
--- /dev/null
+++ b/adf/libadfhwc/include/adfhwc/adfhwc.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBADFHWC_ADFHWC_H_
+#define _LIBADFHWC_ADFHWC_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/cdefs.h>
+#include <video/adf.h>
+
+#include <hardware/hwcomposer.h>
+
+struct adf_hwc_helper;
+
+struct adf_hwc_event_callbacks {
+    /**
+     * Called on vsync (required)
+     */
+    void (*vsync)(void *data, int disp, uint64_t timestamp);
+    /**
+     * Called on hotplug (required)
+     */
+    void (*hotplug)(void *data, int disp, bool connected);
+    /**
+     * Called on hardware-custom ADF events (optional)
+     */
+    void (*custom_event)(void *data, int disp, struct adf_event *event);
+};
+
+/**
+ * Converts HAL pixel formats to equivalent ADF/DRM format FourCCs.
+ */
+static inline uint32_t adf_fourcc_for_hal_pixel_format(int format)
+{
+    switch (format) {
+    case HAL_PIXEL_FORMAT_RGBA_8888:
+        return DRM_FORMAT_RGBA8888;
+    case HAL_PIXEL_FORMAT_RGBX_8888:
+        return DRM_FORMAT_RGBX8888;
+    case HAL_PIXEL_FORMAT_RGB_888:
+        return DRM_FORMAT_RGB888;
+    case HAL_PIXEL_FORMAT_RGB_565:
+        return DRM_FORMAT_RGB565;
+    case HAL_PIXEL_FORMAT_BGRA_8888:
+        return DRM_FORMAT_BGRA8888;
+    case HAL_PIXEL_FORMAT_YV12:
+        return DRM_FORMAT_YVU420;
+    case HAL_PIXEL_FORMAT_YCbCr_422_SP:
+        return DRM_FORMAT_NV16;
+    case HAL_PIXEL_FORMAT_YCrCb_420_SP:
+        return DRM_FORMAT_NV21;
+    case HAL_PIXEL_FORMAT_YCbCr_422_I:
+        return DRM_FORMAT_YUYV;
+    default:
+        return 0;
+    }
+}
+
+/**
+ * Converts HAL display types to equivalent ADF interface flags.
+ */
+static inline uint32_t adf_hwc_interface_flag_for_disp(int disp)
+{
+    switch (disp) {
+    case HWC_DISPLAY_PRIMARY:
+        return ADF_INTF_FLAG_PRIMARY;
+    case HWC_DISPLAY_EXTERNAL:
+        return ADF_INTF_FLAG_EXTERNAL;
+    default:
+        return 0;
+    }
+}
+
+__BEGIN_DECLS
+
+/**
+ * Create a HWC helper for the specified ADF interfaces.
+ *
+ * intf_fds must be indexed by HWC display type: e.g.,
+ * intf_fds[HWC_DISPLAY_PRIMARY] is the fd for the primary display
+ * interface.  n_intfs must be >= 1.
+ *
+ * The caller retains ownership of the fds in intf_fds and must close()
+ * them when they are no longer needed.
+ *
+ * On error, returns -errno.
+ */
+int adf_hwc_open(int *intf_fds, size_t n_intfs,
+        const struct adf_hwc_event_callbacks *event_cb, void *event_cb_data,
+        struct adf_hwc_helper **dev);
+
+/**
+ * Destroys a HWC helper.
+ */
+void adf_hwc_close(struct adf_hwc_helper *dev);
+
+/**
+ * Generic implementations of common HWC ops.
+ *
+ * The HWC should not point its ops directly at these helpers.  Instead, the HWC
+ * should provide stub ops which call these helpers after converting the
+ * hwc_composer_device_1* to a struct adf_hwc_helper*.
+ */
+int adf_eventControl(struct adf_hwc_helper *dev, int disp, int event,
+        int enabled);
+int adf_blank(struct adf_hwc_helper *dev, int disp, int blank);
+int adf_query_display_types_supported(struct adf_hwc_helper *dev, int *value);
+int adf_getDisplayConfigs(struct adf_hwc_helper *dev, int disp,
+        uint32_t *configs, size_t *numConfigs);
+int adf_getDisplayAttributes(struct adf_hwc_helper *dev, int disp,
+        uint32_t config, const uint32_t *attributes, int32_t *values);
+
+__END_DECLS
+
+#endif /* _LIBADFHWC_ADFHWC_H_ */
diff --git a/debuggerd/arm/machine.c b/debuggerd/arm/machine.c
index 6bcd07e..4ab6026 100644
--- a/debuggerd/arm/machine.c
+++ b/debuggerd/arm/machine.c
@@ -15,18 +15,15 @@
 ** limitations under the License.
 */
 
-#include <stddef.h>
+#include <errno.h>
 #include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <stdio.h>
-#include <errno.h>
-#include <sys/types.h>
 #include <sys/ptrace.h>
-
-#include <corkscrew/ptrace.h>
-
-#include <linux/user.h>
+#include <sys/types.h>
+#include <sys/user.h>
 
 #include "../utility.h"
 #include "../machine.h"
diff --git a/debuggerd/mips/machine.c b/debuggerd/mips/machine.c
index e06a50c..489f3c5 100644
--- a/debuggerd/mips/machine.c
+++ b/debuggerd/mips/machine.c
@@ -26,7 +26,7 @@
 
 #include <corkscrew/ptrace.h>
 
-#include <linux/user.h>
+#include <sys/user.h>
 
 #include "../utility.h"
 #include "../machine.h"
diff --git a/fastbootd/Android.mk b/fastbootd/Android.mk
index 76b28e2..0f32dbf 100644
--- a/fastbootd/Android.mk
+++ b/fastbootd/Android.mk
@@ -16,23 +16,89 @@
 
 include $(CLEAR_VARS)
 
+LOCAL_C_INCLUDES := \
+    external/openssl/include \
+    external/mdnsresponder/mDNSShared \
+    $(LOCAL_PATH)/include \
+    external/zlib/ \
+
 LOCAL_SRC_FILES := \
     config.c \
     commands.c \
+    commands/boot.c \
+    commands/flash.c \
+    commands/partitions.c \
+    commands/virtual_partitions.c \
     fastbootd.c \
     protocol.c \
+    network_discovery.c \
+    socket_client.c \
+    secure.c \
     transport.c \
-    usb_linux_client.c
+    transport_socket.c \
+    trigger.c \
+    usb_linux_client.c \
+    utils.c \
 
 LOCAL_MODULE := fastbootd
 LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter -DFLASH_CERT
+LOCAL_LDFLAGS := -ldl
+
+LOCAL_SHARED_LIBRARIES := \
+    libhardware \
+    libcrypto \
+    libhardware_legacy \
+    libmdnssd
+
+LOCAL_STATIC_LIBRARIES := \
+    libsparse_static \
+    libc \
+    libcutils \
+    libz
+
+#LOCAL_FORCE_STATIC_EXECUTABLE := true
+
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_C_INCLUDES := \
+    external/zlib/
+
+LOCAL_SRC_FILES := \
+    commands/partitions.c \
+    other/gptedit.c \
+    utils.c
+
+LOCAL_MODULE := gptedit
+LOCAL_MODULE_TAGS := optional
 LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter
 
 LOCAL_STATIC_LIBRARIES := \
     libsparse_static \
     libc \
-    libcutils
+    libcutils \
+    libz
 
 LOCAL_FORCE_STATIC_EXECUTABLE := true
 
 include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+
+LOCAL_C_INCLUDES := \
+    $(LOCAL_PATH)/include \
+
+LOCAL_STATIC_LIBRARIES := \
+    $(EXTRA_STATIC_LIBS) \
+    libcutils
+
+LOCAL_SRC_FILES := \
+    other/vendor_trigger.c
+
+LOCAL_MODULE := libvendortrigger.default
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter
+
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/fastbootd/commands.c b/fastbootd/commands.c
index 252f655..d8a601f 100644
--- a/fastbootd/commands.c
+++ b/fastbootd/commands.c
@@ -32,114 +32,304 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/reboot.h>
+#include <fcntl.h>
 
 #include "bootimg.h"
+#include "commands/boot.h"
+#include "commands/flash.h"
+#include "commands/partitions.h"
+#include "commands/virtual_partitions.h"
 #include "debug.h"
 #include "protocol.h"
+#include "trigger.h"
+#include "utils.h"
+
+#define ATAGS_LOCATION "/proc/atags"
 
 static void cmd_boot(struct protocol_handle *phandle, const char *arg)
 {
-#if 0
+    int sz, atags_sz, new_atags_sz;
+    int rv;
     unsigned kernel_actual;
     unsigned ramdisk_actual;
-    static struct boot_img_hdr hdr;
-    char *ptr = ((char*) data);
+    unsigned second_actual;
+    void *kernel_ptr;
+    void *ramdisk_ptr;
+    void *second_ptr;
+    struct boot_img_hdr *hdr;
+    char *ptr = NULL;
+    char *atags_ptr = NULL;
+    char *new_atags = NULL;
+    int data_fd = 0;
 
-    if (sz < sizeof(hdr)) {
+    D(DEBUG, "cmd_boot %s\n", arg);
+
+    if (phandle->download_fd < 0) {
+        fastboot_fail(phandle, "no kernel file");
+        return;
+    }
+
+    atags_ptr = read_atags(ATAGS_LOCATION, &atags_sz);
+    if (atags_ptr == NULL) {
+        fastboot_fail(phandle, "atags read error");
+        goto error;
+    }
+
+    // TODO: With cms we can also verify partition name included as
+    // cms signed attribute
+    if (flash_validate_certificate(phandle->download_fd, &data_fd) != 1) {
+        fastboot_fail(phandle, "Access forbiden you need the certificate");
+        return;
+    }
+
+    sz = get_file_size(data_fd);
+
+    ptr = (char *) mmap(NULL, sz, PROT_READ,
+                        MAP_POPULATE | MAP_PRIVATE, data_fd, 0);
+
+    hdr = (struct boot_img_hdr *) ptr;
+
+    if (ptr == MAP_FAILED) {
+        fastboot_fail(phandle, "internal fastbootd error");
+        goto error;
+    }
+
+    if ((size_t) sz < sizeof(*hdr)) {
         fastboot_fail(phandle, "invalid bootimage header");
-        return;
+        goto error;
     }
 
-    memcpy(&hdr, data, sizeof(hdr));
+    kernel_actual = ROUND_TO_PAGE(hdr->kernel_size, hdr->page_size);
+    ramdisk_actual = ROUND_TO_PAGE(hdr->ramdisk_size, hdr->page_size);
+    second_actual = ROUND_TO_PAGE(hdr->second_size, hdr->page_size);
 
-    /* ensure commandline is terminated */
-    hdr.cmdline[BOOT_ARGS_SIZE-1] = 0;
+    new_atags = (char *) create_atags((unsigned *) atags_ptr, atags_sz, hdr, &new_atags_sz);
 
-    kernel_actual = ROUND_TO_PAGE(hdr.kernel_size);
-    ramdisk_actual = ROUND_TO_PAGE(hdr.ramdisk_size);
+    if (new_atags == NULL) {
+        fastboot_fail(phandle, "atags generate error");
+        goto error;
+    }
+    if (new_atags_sz > 0x4000) {
+        fastboot_fail(phandle, "atags file to large");
+        goto error;
+    }
 
-    if (2048 + kernel_actual + ramdisk_actual < sz) {
+    if ((int) (hdr->page_size + kernel_actual + ramdisk_actual) < sz) {
         fastboot_fail(phandle, "incomplete bootimage");
-        return;
+        goto error;
     }
 
-    /*memmove((void*) KERNEL_ADDR, ptr + 2048, hdr.kernel_size);
-    memmove((void*) RAMDISK_ADDR, ptr + 2048 + kernel_actual, hdr.ramdisk_size);*/
+    kernel_ptr = (void *)((unsigned) ptr + hdr->page_size);
+    ramdisk_ptr = (void *)((unsigned) kernel_ptr + kernel_actual);
+    second_ptr = (void *)((unsigned) ramdisk_ptr + ramdisk_actual);
+
+    D(INFO, "preparing to boot");
+    // Prepares boot physical address. Addresses from header are ignored
+    rv = prepare_boot_linux(hdr->kernel_addr, kernel_ptr, kernel_actual,
+                            hdr->ramdisk_addr, ramdisk_ptr, ramdisk_actual,
+                            hdr->second_addr, second_ptr, second_actual,
+                            hdr->tags_addr, new_atags, ROUND_TO_PAGE(new_atags_sz, hdr->page_size));
+    if (rv < 0) {
+        fastboot_fail(phandle, "kexec prepare failed");
+        goto error;
+    }
 
     fastboot_okay(phandle, "");
-    udc_stop();
 
+    free(atags_ptr);
+    munmap(ptr, sz);
+    free(new_atags);
+    close(data_fd);
 
-    /*boot_linux((void*) KERNEL_ADDR, (void*) TAGS_ADDR,
-           (const char*) hdr.cmdline, LINUX_MACHTYPE,
-           (void*) RAMDISK_ADDR, hdr.ramdisk_size);*/
-#endif
+    D(INFO, "Kexec going to reboot");
+    reboot(LINUX_REBOOT_CMD_KEXEC);
+
+    fastboot_fail(phandle, "reboot error");
+
+    return;
+
+error:
+
+    if (atags_ptr != NULL)
+        free(atags_ptr);
+    if (ptr != NULL)
+        munmap(ptr, sz);
+
 }
 
 static void cmd_erase(struct protocol_handle *phandle, const char *arg)
 {
-#if 0
-    struct ptentry *ptn;
-    struct ptable *ptable;
+    int partition_fd;
+    char path[PATH_MAX];
+    D(DEBUG, "cmd_erase %s\n", arg);
 
-    ptable = flash_get_ptable();
-    if (ptable == NULL) {
+    if (flash_find_entry(arg, path, PATH_MAX)) {
         fastboot_fail(phandle, "partition table doesn't exist");
         return;
     }
 
-    ptn = ptable_find(ptable, arg);
-    if (ptn == NULL) {
-        fastboot_fail(phandle, "unknown partition name");
+    if (path == NULL) {
+        fastboot_fail(phandle, "Couldn't find partition");
         return;
     }
 
-    if (flash_erase(ptn)) {
+    partition_fd = flash_get_partiton(path);
+    if (partition_fd < 0) {
+        fastboot_fail(phandle, "partiton file does not exists");
+    }
+
+    if (flash_erase(partition_fd)) {
+        fastboot_fail(phandle, "failed to erase partition");
+        flash_close(partition_fd);
+        return;
+    }
+
+    if (flash_close(partition_fd) < 0) {
+        D(ERR, "could not close device %s", strerror(errno));
         fastboot_fail(phandle, "failed to erase partition");
         return;
     }
     fastboot_okay(phandle, "");
-#endif
+}
+
+static int GPT_header_location() {
+    const char *location_str = fastboot_getvar("gpt_sector");
+    char *str;
+    int location;
+
+    if (!strcmp("", location_str)) {
+        D(INFO, "GPT location not specified using second sector");
+        return 1;
+    }
+    else {
+        location = strtoul(location_str, &str, 10);
+        D(INFO, "GPT location specified as %d", location);
+
+        if (*str != '\0')
+            return -1;
+
+        return location - 1;
+    }
+}
+
+static void cmd_gpt_layout(struct protocol_handle *phandle, const char *arg) {
+    struct GPT_entry_table *oldtable;
+    int location;
+    struct GPT_content content;
+    const char *device;
+    device = fastboot_getvar("blockdev");
+
+    if (!strcmp(device, "")) {
+        fastboot_fail(phandle, "blockdev not defined in config file");
+        return;
+    }
+
+    //TODO: add same verification as in cmd_flash
+    if (phandle->download_fd < 0) {
+        fastboot_fail(phandle, "no layout file");
+        return;
+    }
+
+    location = GPT_header_location();
+    oldtable = GPT_get_device(device, location);
+
+    GPT_default_content(&content, oldtable);
+    if (oldtable == NULL)
+        D(WARN, "Could not get old gpt table");
+    else
+        GPT_release_device(oldtable);
+
+    if (!GPT_parse_file(phandle->download_fd, &content)) {
+        fastboot_fail(phandle, "Could not parse partition config file");
+        return;
+    }
+
+    if (trigger_gpt_layout(&content)) {
+        fastboot_fail(phandle, "Vendor forbids this opperation");
+        GPT_release_content(&content);
+        return;
+    }
+
+    if (!GPT_write_content(device, &content)) {
+        fastboot_fail(phandle, "Unable to write gpt file");
+        GPT_release_content(&content);
+        return;
+    }
+
+    GPT_release_content(&content);
+    fastboot_okay(phandle, "");
 }
 
 static void cmd_flash(struct protocol_handle *phandle, const char *arg)
 {
-#if 0
-    struct ptentry *ptn;
-    struct ptable *ptable;
-    unsigned extra = 0;
+    int partition;
+    uint64_t sz;
+    char data[BOOT_MAGIC_SIZE];
+    char path[PATH_MAX];
+    ssize_t header_sz = 0;
+    int data_fd = 0;
 
-    ptable = flash_get_ptable();
-    if (ptable == NULL) {
+    D(DEBUG, "cmd_flash %s\n", arg);
+
+    if (try_handle_virtual_partition(phandle, arg)) {
+        return;
+    }
+
+    if (phandle->download_fd < 0) {
+        fastboot_fail(phandle, "no kernel file");
+        return;
+    }
+
+    if (flash_find_entry(arg, path, PATH_MAX)) {
         fastboot_fail(phandle, "partition table doesn't exist");
         return;
     }
 
-    ptn = ptable_find(ptable, arg);
-    if (ptn == NULL) {
-        fastboot_fail(phandle, "unknown partition name");
+    if (flash_validate_certificate(phandle->download_fd, &data_fd) != 1) {
+        fastboot_fail(phandle, "Access forbiden you need certificate");
         return;
     }
 
-    if (!strcmp(ptn->name, "boot") || !strcmp(ptn->name, "recovery")) {
+    // TODO: Maybe its goot idea to check whether the partition is bootable
+    if (!strcmp(arg, "boot") || !strcmp(arg, "recovery")) {
+        if (read_data_once(data_fd, data, BOOT_MAGIC_SIZE) < BOOT_MAGIC_SIZE) {
+            fastboot_fail(phandle, "incoming data read error, cannot read boot header");
+            return;
+        }
         if (memcmp((void *)data, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
             fastboot_fail(phandle, "image is not a boot image");
             return;
         }
     }
 
-    if (!strcmp(ptn->name, "system") || !strcmp(ptn->name, "userdata"))
-        extra = 64;
-    else
-        sz = ROUND_TO_PAGE(sz);
+    partition = flash_get_partiton(path);
 
-    D(INFO, "writing %d bytes to '%s'\n", sz, ptn->name);
-    if (flash_write(ptn, extra, data, sz)) {
+    sz = get_file_size64(data_fd);
+
+    sz -= header_sz;
+
+    if (sz > get_file_size64(partition)) {
+        flash_close(partition);
+        D(WARN, "size of file too large");
+        fastboot_fail(phandle, "size of file too large");
+        return;
+    }
+
+    D(INFO, "writing %lld bytes to '%s'\n", sz, arg);
+
+    if (flash_write(partition, phandle->download_fd, sz, header_sz)) {
         fastboot_fail(phandle, "flash write failure");
         return;
     }
-    D(INFO, "partition '%s' updated\n", ptn->name);
-#endif
+    D(INFO, "partition '%s' updated\n", arg);
+
+    flash_close(partition);
+    close(data_fd);
+
     fastboot_okay(phandle, "");
 }
 
@@ -184,7 +374,6 @@
 
     phandle->download_fd = protocol_handle_download(phandle, len);
     if (phandle->download_fd < 0) {
-        //handle->state = STATE_ERROR;
         fastboot_fail(phandle, "download failed");
         return;
     }
@@ -192,14 +381,27 @@
     fastboot_okay(phandle, "");
 }
 
+static void cmd_oem(struct protocol_handle *phandle, const char *arg) {
+    const char *response = "";
+
+    //TODO: Maybe it should get download descriptor also
+    if (trigger_oem_cmd(arg, &response))
+        fastboot_fail(phandle, response);
+    else
+        fastboot_okay(phandle, response);
+}
+
 void commands_init()
 {
+    virtual_partition_register("partition-table", cmd_gpt_layout);
+
     fastboot_register("boot", cmd_boot);
     fastboot_register("erase:", cmd_erase);
     fastboot_register("flash:", cmd_flash);
     fastboot_register("continue", cmd_continue);
     fastboot_register("getvar:", cmd_getvar);
     fastboot_register("download:", cmd_download);
+    fastboot_register("oem", cmd_oem);
     //fastboot_publish("version", "0.5");
     //fastboot_publish("product", "swordfish");
     //fastboot_publish("kernel", "lk");
diff --git a/fastbootd/commands/boot.c b/fastbootd/commands/boot.c
new file mode 100644
index 0000000..8da9a28
--- /dev/null
+++ b/fastbootd/commands/boot.c
@@ -0,0 +1,254 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/syscall.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "boot.h"
+#include "debug.h"
+#include "utils.h"
+#include "bootimg.h"
+
+
+#define KEXEC_ARM_ATAGS_OFFSET  0x1000
+#define KEXEC_ARM_ZIMAGE_OFFSET 0x8000
+
+#define MEMORY_SIZE 0x0800000
+#define START_ADDRESS 0x44000000
+#define KERNEL_START (START_ADDRESS + KEXEC_ARM_ZIMAGE_OFFSET)
+
+#define ATAG_NONE_TYPE      0x00000000
+#define ATAG_CORE_TYPE      0x54410001
+#define ATAG_RAMDISK_TYPE   0x54410004
+#define ATAG_INITRD2_TYPE   0x54420005
+#define ATAG_CMDLINE_TYPE   0x54410009
+
+#define MAX_ATAG_SIZE 0x4000
+
+struct atag_info {
+    unsigned size;
+    unsigned type;
+};
+
+struct atag_initrd2 {
+    unsigned start;
+    unsigned size;
+};
+
+struct atag_cmdline {
+    char cmdline[0];
+};
+
+struct atag {
+    struct atag_info info;
+    union {
+        struct atag_initrd2 initrd2;
+        struct atag_cmdline cmdline;
+    } data;
+};
+
+
+long kexec_load(unsigned int entry, unsigned long nr_segments,
+                struct kexec_segment *segment, unsigned long flags) {
+   return syscall(__NR_kexec_load, entry, nr_segments, segment, flags);
+}
+
+/*
+ * Prepares arguments for kexec
+ * Kernel address is not set into kernel_phys
+ * Ramdisk is set to position relative to kernel
+ */
+int prepare_boot_linux(unsigned kernel_phys, void *kernel_addr, int kernel_size,
+                       unsigned ramdisk_phys, void *ramdisk_addr, int ramdisk_size,
+                       unsigned second_phys, void *second_addr, int second_size,
+                       unsigned atags_phys, void *atags_addr, int atags_size) {
+    struct kexec_segment segment[4];
+    int segment_count = 2;
+    unsigned entry = START_ADDRESS + KEXEC_ARM_ZIMAGE_OFFSET;
+    int rv;
+    int page_size = getpagesize();
+
+    segment[0].buf = kernel_addr;
+    segment[0].bufsz = kernel_size;
+    segment[0].mem = (void *) KERNEL_START;
+    segment[0].memsz = ROUND_TO_PAGE(kernel_size, page_size);
+
+    if (kernel_size > MEMORY_SIZE - KEXEC_ARM_ZIMAGE_OFFSET) {
+        D(INFO, "Kernel image too big");
+        return -1;
+    }
+
+    segment[1].buf = atags_addr;
+    segment[1].bufsz = atags_size;
+    segment[1].mem = (void *) (START_ADDRESS + KEXEC_ARM_ATAGS_OFFSET);
+    segment[1].memsz = ROUND_TO_PAGE(atags_size, page_size);
+
+    D(INFO, "Ramdisk size is %d", ramdisk_size);
+
+    if (ramdisk_size != 0) {
+        segment[segment_count].buf = ramdisk_addr;
+        segment[segment_count].bufsz = ramdisk_size;
+        segment[segment_count].mem = (void *) (KERNEL_START + ramdisk_phys - kernel_phys);
+        segment[segment_count].memsz = ROUND_TO_PAGE(ramdisk_phys, page_size);
+        ++segment_count;
+    }
+
+    D(INFO, "Ramdisk size is %d", ramdisk_size);
+    if (second_size != 0) {
+        segment[segment_count].buf = second_addr;
+        segment[segment_count].bufsz = second_size;
+        segment[segment_count].mem = (void *) (KERNEL_START + second_phys - kernel_phys);
+        segment[segment_count].memsz = ROUND_TO_PAGE(second_size, page_size);
+        entry = second_phys;
+        ++segment_count;
+    }
+
+    rv = kexec_load(entry, segment_count, segment, KEXEC_ARCH_DEFAULT);
+
+    if (rv != 0) {
+        D(INFO, "Kexec_load returned non-zero exit code: %s\n", strerror(errno));
+        return -1;
+    }
+
+    return 1;
+
+}
+
+unsigned *create_atags(unsigned *atags_position, int atag_size, const struct boot_img_hdr *hdr, int *size) {
+    struct atag *current_tag = (struct atag *) atags_position;
+    unsigned *current_tag_raw = atags_position;
+    unsigned *new_atags = malloc(ROUND_TO_PAGE(atag_size + BOOT_ARGS_SIZE * sizeof(char),
+                                               hdr->page_size));
+    //This pointer will point into the beggining of buffer free space
+    unsigned *natags_raw_buff = new_atags;
+    int new_atags_size = 0;
+    int current_size;
+    int cmdl_length;
+
+    // copy tags from current atag file
+    while (current_tag->info.type != ATAG_NONE_TYPE) {
+        switch (current_tag->info.type) {
+            case ATAG_CMDLINE_TYPE:
+            case ATAG_RAMDISK_TYPE:
+            case ATAG_INITRD2_TYPE: break;
+            default:
+                memcpy((void *)natags_raw_buff, (void *)current_tag_raw, current_tag->info.size * sizeof(unsigned));
+                natags_raw_buff += current_tag->info.size;
+                new_atags_size += current_tag->info.size;
+        }
+
+        current_tag_raw += current_tag->info.size;
+        current_tag = (struct atag *) current_tag_raw;
+
+        if (current_tag_raw >= atags_position + atag_size) {
+            D(ERR, "Critical error in atags");
+            return NULL;
+        }
+    }
+
+    // set INITRD2 tag
+    if (hdr->ramdisk_size > 0) {
+        current_size = (sizeof(struct atag_info) + sizeof(struct atag_initrd2)) / sizeof(unsigned);
+        *((struct atag *) natags_raw_buff) = (struct atag) {
+            .info = {
+                .size = current_size,
+                .type = ATAG_INITRD2_TYPE
+            },
+            .data = {
+                .initrd2 = (struct atag_initrd2) {
+                    .start = hdr->ramdisk_addr,
+                    .size = hdr->ramdisk_size
+                }
+            }
+        };
+
+        new_atags_size += current_size;
+        natags_raw_buff += current_size;
+    }
+
+    // set ATAG_CMDLINE
+    cmdl_length = strnlen((char *) hdr->cmdline, BOOT_ARGS_SIZE - 1);
+    current_size = sizeof(struct atag_info) + (1 + cmdl_length);
+    current_size = (current_size + sizeof(unsigned) - 1) / sizeof(unsigned);
+    *((struct atag *) natags_raw_buff) = (struct atag) {
+        .info = {
+            .size = current_size,
+            .type = ATAG_CMDLINE_TYPE
+        },
+    };
+
+    //copy cmdline and ensure that there is null character
+    memcpy(((struct atag *) natags_raw_buff)->data.cmdline.cmdline,
+           (char *) hdr->cmdline, cmdl_length);
+    ((struct atag *) natags_raw_buff)->data.cmdline.cmdline[cmdl_length] = '\0';
+
+    new_atags_size += current_size;
+    natags_raw_buff += current_size;
+
+    // set ATAG_NONE
+    *((struct atag *) natags_raw_buff) = (struct atag) {
+        .info = {
+            .size = 0,
+            .type = ATAG_NONE_TYPE
+        },
+    };
+    new_atags_size += sizeof(struct atag_info) / sizeof(unsigned);
+    natags_raw_buff += sizeof(struct atag_info) / sizeof(unsigned);
+
+    *size = new_atags_size * sizeof(unsigned);
+    return new_atags;
+}
+
+char *read_atags(const char * path, int *atags_sz) {
+    int afd = -1;
+    char *atags_ptr = NULL;
+
+    afd = open(path, O_RDONLY);
+    if (afd < 0) {
+        D(ERR, "wrong atags file");
+        return 0;
+    }
+
+    atags_ptr = (char *) malloc(MAX_ATAG_SIZE);
+    if (atags_ptr == NULL) {
+        D(ERR, "insufficient memory");
+        return 0;
+    }
+
+    *atags_sz = read(afd, atags_ptr, MAX_ATAG_SIZE);
+
+    close(afd);
+    return atags_ptr;
+}
+
diff --git a/fastbootd/commands/boot.h b/fastbootd/commands/boot.h
new file mode 100644
index 0000000..901c38a
--- /dev/null
+++ b/fastbootd/commands/boot.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef __FASTBOOT_BOOT_H
+#define __FASTBOOT_BOOT_H
+
+#include <sys/cdefs.h>
+#include <linux/kexec.h>
+
+#include "bootimg.h"
+
+#define KEXEC_TYPE_DEFAULT 0
+#define KEXEC_TYPE_CRASH   1
+
+int prepare_boot_linux(unsigned, void *, int, unsigned, void *, int,
+                       unsigned, void *, int, unsigned, void *, int);
+unsigned *create_atags(unsigned *, int, const struct boot_img_hdr *, int *);
+long kexec_load(unsigned int, unsigned long, struct kexec_segment *, unsigned long);
+char *read_atags(const char *, int *);
+
+#endif /* _SYS_KEXEC_H */
+
diff --git a/fastbootd/commands/flash.c b/fastbootd/commands/flash.c
new file mode 100644
index 0000000..0954217
--- /dev/null
+++ b/fastbootd/commands/flash.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+
+#include "flash.h"
+#include "protocol.h"
+#include "debug.h"
+#include "utils.h"
+#include "commands/partitions.h"
+
+#ifdef FLASH_CERT
+#include "secure.h"
+#endif
+
+#define ALLOWED_CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-."
+#define BUFFER_SIZE 1024 * 1024
+#define MIN(a, b) (a > b ? b : a)
+
+
+int flash_find_entry(const char *name, char *out, size_t outlen)
+{
+//TODO: Assumption: All the partitions has they unique name
+
+    const char *path = fastboot_getvar("device-directory");
+    size_t length;
+    if (strcmp(path, "") == 0) {
+        D(ERR, "device-directory: not defined in config file");
+        return -1;
+    }
+
+    length = strspn(name, ALLOWED_CHARS);
+    if (length != strlen(name)) {
+        D(ERR, "Not allowed char in name: %c", name[length]);
+        return -1;
+    }
+
+    if (snprintf(out, outlen, "%s%s", path, name) >= (int) outlen) {
+        D(ERR, "Too long path to partition file");
+        return -1;
+    }
+
+    if (access(out, F_OK ) == -1) {
+        D(ERR, "could not find partition file %s", name);
+        return -1;
+    }
+
+    return 0;
+}
+
+int flash_erase(int fd)
+{
+    int64_t size;
+    size = get_block_device_size(fd);
+    D(DEBUG, "erase %llu data from %d\n", size, fd);
+
+    return wipe_block_device(fd, size);
+}
+
+int flash_write(int partition_fd, int data_fd, ssize_t size, ssize_t skip)
+{
+    ssize_t written = 0;
+    struct GPT_mapping input;
+    struct GPT_mapping output;
+
+    while (written < size) {
+        int current_size = MIN(size - written, BUFFER_SIZE);
+
+        if (gpt_mmap(&input, written + skip, current_size, data_fd)) {
+            D(ERR, "Error in writing data, unable to map data file %d at %d size %d", size, skip, current_size);
+            return -1;
+        }
+        if (gpt_mmap(&output, written, current_size, partition_fd)) {
+            D(ERR, "Error in writing data, unable to map output partition");
+            return -1;
+        }
+
+        memcpy(output.ptr, input.ptr, current_size);
+
+        gpt_unmap(&input);
+        gpt_unmap(&output);
+
+        written += current_size;
+    }
+
+    return 0;
+}
+
+#ifdef FLASH_CERT
+
+int flash_validate_certificate(int signed_fd, int *data_fd) {
+    int ret = 0;
+    const char *cert_path;
+    X509_STORE *store = NULL;
+    CMS_ContentInfo *content_info;
+    BIO *content;
+
+    cert_path = fastboot_getvar("certificate-path");
+    if (!strcmp(cert_path, "")) {
+        D(ERR, "could not find cert-key value in config file");
+        goto finish;
+    }
+
+    store = cert_store_from_path(cert_path);
+    if (store == NULL) {
+        D(ERR, "unable to create certification store");
+        goto finish;
+    }
+
+    if (cert_read(signed_fd, &content_info, &content)) {
+        D(ERR, "reading data failed");
+        goto finish;
+    }
+
+    ret = cert_verify(content, content_info, store, data_fd);
+    cert_release(content, content_info);
+
+    return ret;
+
+finish:
+    if (store != NULL)
+        cert_release_store(store);
+
+    return ret;
+}
+
+#else
+int flash_validate_certificate(int signed_fd, int *data_fd) {
+    return 1;
+}
+#endif
diff --git a/fastbootd/commands/flash.h b/fastbootd/commands/flash.h
new file mode 100644
index 0000000..5a64cab
--- /dev/null
+++ b/fastbootd/commands/flash.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _FASTBOOTD_ERASE_H
+#define _FASTBOOTD_ERASE_H
+
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include "debug.h"
+
+int flash_find_entry(const char *, char *, size_t);
+int flash_erase(int fd);
+
+static inline int flash_get_partiton(const char *path) {
+    return open(path, O_RDWR);
+}
+
+static inline int flash_close(int fd) {
+    return close(fd);
+}
+
+int flash_write(int partition, int data, ssize_t size, ssize_t skip);
+
+static inline ssize_t read_data_once(int fd, char *buffer, ssize_t size) {
+    ssize_t readcount = 0;
+    ssize_t len;
+
+    while ((len = TEMP_FAILURE_RETRY(read(fd, (void *) &buffer[readcount], size - readcount))) > 0) {
+        readcount += len;
+    }
+    if (len < 0) {
+        D(ERR, "Read error:%s", strerror(errno));
+        return len;
+    }
+
+    return readcount;
+}
+
+int flash_validate_certificate(int signed_fd, int *data_fd);
+
+#endif
+
diff --git a/fastbootd/commands/partitions.c b/fastbootd/commands/partitions.c
new file mode 100644
index 0000000..de80ea3
--- /dev/null
+++ b/fastbootd/commands/partitions.c
@@ -0,0 +1,771 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <endian.h>
+#include <zlib.h>
+#include <linux/hdreg.h>
+#include <sys/ioctl.h>
+#include <stdlib.h>
+#include <cutils/config_utils.h>
+
+#include "partitions.h"
+#include "debug.h"
+#include "utils.h"
+#include "protocol.h"
+
+#define BLKRRPART  _IO(0x12,95) /* re-read partition table */
+#define BLKSSZGET  _IO(0x12,104)
+
+#define DIV_ROUND_UP(x, y) (((x) + (y) - 1)/(y))
+#define ALIGN(x, y) ((y) * DIV_ROUND_UP((x), (y)))
+#define ALIGN_DOWN(x, y) ((y) * ((x) / (y)))
+
+
+const uint8_t partition_type_uuid[16] = {
+    0xa2, 0xa0, 0xd0, 0xeb, 0xe5, 0xb9, 0x33, 0x44,
+    0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7,
+};
+
+//TODO: There is assumption that we are using little endian
+
+static void GPT_entry_clear(struct GPT_entry_raw *entry)
+{
+    memset(entry, 0, sizeof(*entry));
+}
+
+/*
+ * returns mapped location to choosen area
+ * mapped_ptr is pointer to whole area mapped (it can be bigger then requested)
+ */
+int gpt_mmap(struct GPT_mapping *mapping, uint64_t location, int size, int fd)
+{
+    unsigned int location_diff = location & ~PAGE_MASK;
+
+    mapping->size = ALIGN(size + location_diff, PAGE_SIZE);
+
+    uint64_t sz = get_file_size64(fd);
+    if (sz < size + location) {
+        D(ERR, "the location of mapping area is outside of the device size %lld", sz);
+        return 1;
+    }
+    location = ALIGN_DOWN(location, PAGE_SIZE);
+
+    mapping->map_ptr = mmap64(NULL, mapping->size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, location);
+
+    if (mapping->map_ptr == MAP_FAILED) {
+        mapping->ptr = MAP_FAILED;
+        D(ERR, "map failed %d", (int) mapping->map_ptr);
+        return 1;
+    }
+
+    mapping->ptr = (void *)((char *) mapping->map_ptr + location_diff);
+    return 0;
+}
+
+void gpt_unmap(struct GPT_mapping *mapping) {
+    munmap(mapping->map_ptr, mapping->size);
+}
+
+
+#define LBA_ADDR(table, value)   ((uint64_t) (table)->sector_size * (value))
+
+int GPT_map_from_content(struct GPT_entry_table *table, const struct GPT_content *content)
+{
+
+    // Mapping header
+    if (gpt_mmap(&table->header_map, LBA_ADDR(table, content->header.current_lba),
+                 table->sector_size, table->fd)) {
+        D(ERR, "unable to map header:%s\n", strerror(errno));
+        goto error_header;
+    }
+
+    table->header = (struct GPT_header *) table->header_map.ptr;
+
+    table->partition_table_size = ROUND_UP(content->header.entries_count * sizeof(*table->entries),
+                                           table->sector_size);
+
+    // Mapping entry table
+    if (gpt_mmap(&table->entries_map, LBA_ADDR(table, content->header.entries_lba),
+                 table->partition_table_size, table->fd)) {
+        D(ERR, "unable to map entries");
+        goto error_signature;
+    }
+
+    table->entries = (struct GPT_entry_raw *) table->entries_map.ptr;
+
+    // Mapping secondary header
+    if (gpt_mmap(&table->sec_header_map, LBA_ADDR(table, content->header.backup_lba),
+                 table->sector_size, table->fd)) {
+        D(ERR, "unable to map backup gpt header");
+        goto error_sec_header;
+    }
+
+    // Mapping secondary entries table
+    if (gpt_mmap(&table->sec_entries_map,
+                 LBA_ADDR(table, content->header.backup_lba) - table->partition_table_size,
+                 table->partition_table_size, table->fd)) {
+        D(ERR, "unable to map secondary gpt table");
+        goto error_sec_entries;
+    }
+
+    table->second_header = (struct GPT_header *) table->sec_header_map.ptr;
+    table->second_entries = (struct GPT_entry_raw *) table->sec_entries_map.ptr;
+    table->second_valid = strcmp("EFI PART", (char *) table->second_header->signature) == 0;
+
+    return 0;
+
+error_sec_entries:
+    gpt_unmap(&table->sec_header_map);
+error_sec_header:
+    gpt_unmap(&table->entries_map);
+error_signature:
+    gpt_unmap(&table->header_map);
+error_header:
+    return 1;
+}
+
+int GPT_map(struct GPT_entry_table *table, unsigned header_lba)
+{
+    struct GPT_content content;
+    struct GPT_mapping mapping;
+    struct GPT_header *header;
+
+    if (gpt_mmap(&mapping, LBA_ADDR(table, header_lba), table->sector_size, table->fd)) {
+        D(ERR, "unable to map header: %s", strerror(errno));
+        goto error_header;
+    }
+
+    header = (struct GPT_header *) mapping.ptr;
+
+    if (strcmp("EFI PART", (char *) header->signature)) {
+        D(ERR, "GPT entry not valid");
+        goto error_signature;
+    }
+
+    content.header = *header;
+
+    gpt_unmap(&mapping);
+
+    return GPT_map_from_content(table, &content);
+
+error_signature:
+    gpt_unmap(&table->header_map);
+error_header:
+    return 1;
+}
+
+struct GPT_entry_table* GPT_get_device(const char *path, unsigned header_lba)
+{
+    struct GPT_entry_table *table;
+    size_t sector_bytes;
+
+    table = (struct GPT_entry_table *) malloc(sizeof(*table));
+    table->fd = open(path, O_RDWR);
+
+    if (table->fd < 0) {
+        D(ERR, "unable to open file %s:%s\n", path, strerror(errno));
+        return NULL;
+    }
+
+    if (!ioctl(table->fd, BLKSSZGET, &sector_bytes)) {
+        table->sector_size = (unsigned) sector_bytes;
+        D(INFO, "Got sector size %d", table->sector_size);
+    } else {
+        D(WARN, "unable to get sector size, assuming 512");
+        table->sector_size = 512;
+    }
+
+    if (GPT_map(table, header_lba)) {
+        D(ERR, "Could not map gpt");
+        return NULL;
+    }
+
+    return table;
+}
+
+static struct GPT_entry_table* GPT_get_from_content(const char *path, const struct GPT_content *content)
+{
+    struct GPT_entry_table *table;
+    size_t sector_bytes;
+
+    table = (struct GPT_entry_table *) malloc(sizeof(*table));
+    table->fd = open(path, O_RDWR);
+
+    if (table->fd < 0) {
+        D(ERR, "unable to open file %s:%s\n", path, strerror(errno));
+        return NULL;
+    }
+
+    if (!ioctl(table->fd, BLKSSZGET, &sector_bytes)) {
+        table->sector_size = (unsigned) sector_bytes;
+        D(INFO, "Got sector size %d", table->sector_size);
+    } else {
+        D(WARN, "unable to get sector size %s, assuming 512", strerror(errno));
+        table->sector_size = 512;
+    }
+
+    if (GPT_map_from_content(table, content)) {
+        D(ERR, "Could not map gpt");
+        return NULL;
+    }
+
+    return table;
+}
+
+
+void GPT_release_device(struct GPT_entry_table *table)
+{
+    gpt_unmap(&table->header_map);
+    gpt_unmap(&table->entries_map);
+    gpt_unmap(&table->sec_header_map);
+    gpt_unmap(&table->sec_entries_map);
+    close(table->fd);
+    free(table);
+}
+
+static int GPT_check_overlap(struct GPT_entry_table *table, struct GPT_entry_raw *entry);
+static int GPT_check_overlap_except(struct GPT_entry_table *table,
+                                    struct GPT_entry_raw *entry,
+                                    struct GPT_entry_raw *exclude);
+
+void GPT_edit_entry(struct GPT_entry_table *table,
+                    struct GPT_entry_raw *old_entry,
+                    struct GPT_entry_raw *new_entry)
+{
+    struct GPT_entry_raw *current_entry = GPT_get_pointer(table, old_entry);
+
+    if (GPT_check_overlap_except(table, new_entry, current_entry)) {
+        D(ERR, "Couldn't add overlaping partition");
+        return;
+    }
+
+    if (current_entry == NULL) {
+        D(ERR, "Couldn't find entry");
+        return;
+    }
+
+    *current_entry = *new_entry;
+}
+
+int GPT_delete_entry(struct GPT_entry_table *table, struct GPT_entry_raw *entry)
+{
+    struct GPT_entry_raw *raw = GPT_get_pointer(table, entry);
+
+    if (raw == NULL) {
+        D(ERR, "could not find entry");
+        return 1;
+    }
+    D(DEBUG, "Deleting gpt entry '%s'\n", raw->partition_guid);
+
+    // Entry in the middle of table may become empty
+    GPT_entry_clear(raw);
+
+    return 0;
+}
+
+void GPT_add_entry(struct GPT_entry_table *table, struct GPT_entry_raw *entry)
+{
+    unsigned i;
+    int inserted = 0;
+    if (GPT_check_overlap(table, entry)) {
+        D(ERR, "Couldn't add overlaping partition");
+        return;
+    }
+
+    if (GPT_get_pointer(table, entry) != NULL) {
+        D(WARN, "Add entry fault, this entry already exists");
+        return;
+    }
+
+    struct GPT_entry_raw *entries = table->entries;
+
+    for (i = 0; i < table->header->entries_count; ++i) {
+        if (!entries[i].type_guid[0]) {
+            inserted = 1;
+            D(DEBUG, "inserting");
+            memcpy(&entries[i], entry, sizeof(entries[i]));
+            break;
+        }
+    }
+
+    if (!inserted) {
+        D(ERR, "Unable to find empty partion entry");
+    }
+}
+
+struct GPT_entry_raw *GPT_get_pointer_by_UTFname(struct GPT_entry_table *table, const uint16_t *name);
+
+struct GPT_entry_raw *GPT_get_pointer(struct GPT_entry_table *table, struct GPT_entry_raw *entry)
+{
+    if (entry->partition_guid[0] != 0)
+        return GPT_get_pointer_by_guid(table, (const char *) entry->partition_guid);
+    else if (entry->name[0] != 0)
+        return GPT_get_pointer_by_UTFname(table, entry->name);
+
+    D(WARN, "Name or guid needed to find entry");
+    return NULL;
+}
+
+struct GPT_entry_raw *GPT_get_pointer_by_guid(struct GPT_entry_table *table, const char *name)
+{
+    int current = (int) table->header->entries_count;
+
+    for (current = current - 1; current >= 0; --current) {
+        if (strncmp((char *) name,
+                    (char *) table->entries[current].partition_guid, 16) == 0) {
+                return &table->entries[current];
+        }
+    }
+
+    return NULL;
+}
+
+int strncmp_UTF16_char(const uint16_t *s1, const char *s2, size_t n)
+{
+    if (n == 0)
+        return (0);
+    do {
+        if (((*s1) & 127) != *s2++)
+            return (((unsigned char) ((*s1) & 127)) - *(unsigned char *)--s2);
+        if (*s1++ == 0)
+            break;
+    } while (--n != 0);
+    return (0);
+}
+
+int strncmp_UTF16(const uint16_t *s1, const uint16_t *s2, size_t n)
+{
+    if (n == 0)
+        return (0);
+    do {
+        if ((*s1) != *s2++)
+            return (*s1 - *--s2);
+        if (*s1++ == 0)
+            break;
+    } while (--n != 0);
+    return (0);
+}
+
+struct GPT_entry_raw *GPT_get_pointer_by_name(struct GPT_entry_table *table, const char *name)
+{
+    int count = (int) table->header->entries_count;
+    int current;
+
+    for (current = 0; current < count; ++current) {
+        if (strncmp_UTF16_char(table->entries[current].name,
+                         (char *) name, 16) == 0) {
+                    return &table->entries[current];
+        }
+    }
+
+    return NULL;
+}
+
+struct GPT_entry_raw *GPT_get_pointer_by_UTFname(struct GPT_entry_table *table, const uint16_t *name)
+{
+    int count = (int) table->header->entries_count;
+    int current;
+
+    for (current = 0; current < count; ++current) {
+        if (strncmp_UTF16(table->entries[current].name,
+                          name, GPT_NAMELEN) == 0) {
+                return &table->entries[current];
+        }
+    }
+
+    return NULL;
+}
+
+void GPT_sync(struct GPT_entry_table *table)
+{
+    uint32_t crc;
+
+    //calculate crc32
+    crc = crc32(0, Z_NULL, 0);
+    crc = crc32(crc, (void*) table->entries, table->header->entries_count * sizeof(*table->entries));
+    table->header->partition_array_checksum = crc;
+
+    table->header->header_checksum = 0;
+    crc = crc32(0, Z_NULL, 0);
+    crc = crc32(crc, (void*) table->header, table->header->header_size);
+    table->header->header_checksum = crc;
+
+    //sync secondary partion
+    if (table->second_valid) {
+        memcpy((void *)table->second_entries, (void *) table->entries, table->partition_table_size);
+        memcpy((void *)table->second_header, (void *)table->header, sizeof(*table->header));
+    }
+
+    if(!ioctl(table->fd, BLKRRPART, NULL)) {
+        D(WARN, "Unable to force kernel to refresh partition table");
+    }
+}
+
+void GPT_to_UTF16(uint16_t *to, const char *from, int n)
+{
+    int i;
+    for (i = 0; i < (n - 1) && (to[i] = from[i]) != '\0'; ++i);
+    to[i] = '\0';
+}
+
+void GPT_from_UTF16(char *to, const uint16_t *from, int n)
+{
+    int i;
+    for (i = 0; i < (n - 1) && (to[i] = from[i] & 127) != '\0'; ++i);
+    to[i] = '\0';
+}
+
+static int GPT_check_overlap_except(struct GPT_entry_table *table,
+                                    struct GPT_entry_raw *entry,
+                                    struct GPT_entry_raw *exclude) {
+    int current = (int) table->header->entries_count;
+    int dontcheck;
+    struct GPT_entry_raw *current_entry;
+    if (entry->last_lba < entry->first_lba) {
+        D(WARN, "Start address have to be less than end address");
+        return 1;
+    }
+
+    for (current = current - 1; current >= 0; --current) {
+        current_entry = &table->entries[current];
+        dontcheck = strncmp((char *) entry->partition_guid,
+                           (char *) current_entry->partition_guid , 16) == 0;
+        dontcheck |= current_entry->type_guid[0] == 0;
+        dontcheck |= current_entry == exclude;
+
+        if (!dontcheck && ((entry->last_lba >= current_entry->first_lba &&
+                            entry->first_lba < current_entry->last_lba ))) {
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+static int GPT_check_overlap(struct GPT_entry_table *table, struct GPT_entry_raw *entry)
+{
+    return GPT_check_overlap_except(table, entry, NULL);
+}
+
+static char *get_key_value(char *ptr, char **key, char **value)
+{
+    *key = ptr;
+    ptr = strchr(ptr, '=');
+
+    if (ptr == NULL)
+        return NULL;
+
+    *ptr++ = '\0';
+    *value = ptr;
+    ptr = strchr(ptr, ';');
+
+    if (ptr == NULL)
+        ptr = *value + strlen(*value);
+    else
+        *ptr = '\0';
+
+    *key = strip(*key);
+    *value = strip(*value);
+
+    return ptr;
+}
+
+//TODO: little endian?
+static int add_key_value(const char *key, const char *value, struct GPT_entry_raw *entry)
+{
+    char *endptr;
+    if (!strcmp(key, "type")) {
+        strncpy((char *) entry->type_guid, value, 16);
+        entry->type_guid[15] = 0;
+    }
+    else if (!strcmp(key, "guid")) {
+        strncpy((char *) entry->partition_guid, value, 16);
+        entry->type_guid[15] = 0;
+    }
+    else if (!strcmp(key, "firstlba")) {
+        entry->first_lba = strtoul(value, &endptr, 10);
+        if (*endptr != '\0') goto error;
+    }
+    else if (!strcmp(key, "lastlba")) {
+        entry->last_lba = strtoul(value, &endptr, 10);
+        if (*endptr != '\0') goto error;
+    }
+    else if (!strcmp(key, "flags")) {
+        entry->flags = strtoul(value, &endptr, 16);
+        if (*endptr != '\0') goto error;
+    }
+    else if (!strcmp(key, "name")) {
+        GPT_to_UTF16(entry->name, value, GPT_NAMELEN);
+    }
+    else {
+        goto error;
+    }
+
+    return 0;
+
+error:
+    D(ERR, "Could not find key or parse value: %s,%s", key, value);
+    return 1;
+}
+
+int GPT_parse_entry(char *string, struct GPT_entry_raw *entry)
+{
+    char *ptr = string;
+    char *key, *value;
+
+    while ((ptr = get_key_value(ptr, &key, &value)) != NULL) {
+        if (add_key_value(key, value, entry)) {
+            D(WARN, "key or value not valid: %s %s", key, value);
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+void entry_set_guid(int n, uint8_t *guid)
+{
+    int fd;
+    fd = open("/dev/urandom", O_RDONLY);
+    read(fd, guid, 16);
+    close(fd);
+
+    //rfc4122
+    guid[8] = (guid[8] & 0x3F) | 0x80;
+    guid[7] = (guid[7] & 0x0F) | 0x40;
+}
+
+void GPT_default_content(struct GPT_content *content, struct GPT_entry_table *table)
+{
+    if (table != NULL) {
+        memcpy(&content->header, table->header, sizeof(content->header));
+        content->header.header_size = sizeof(content->header);
+        content->header.entry_size = sizeof(struct GPT_entry_raw);
+    }
+    else {
+        D(WARN, "Could not locate old gpt table, using default values");
+        memset(&content->header, 0, sizeof(content->header) / sizeof(int));
+        content->header = (struct GPT_header) {
+            .revision = 0x10000,
+            .header_size = sizeof(content->header),
+            .header_checksum = 0,
+            .reserved_zeros = 0,
+            .current_lba = 1,
+            .backup_lba = 1,
+            .entry_size = sizeof(struct GPT_entry_raw),
+            .partition_array_checksum = 0
+        };
+        strncpy((char *)content->header.signature, "EFI PART", 8);
+        strncpy((char *)content->header.disk_guid, "ANDROID MMC DISK", 16);
+    }
+}
+
+static int get_config_uint64(cnode *node, uint64_t *ptr, const char *name)
+{
+    const char *tmp;
+    uint64_t val;
+    char *endptr;
+    if ((tmp = config_str(node, name, NULL))) {
+        val = strtoull(tmp, &endptr, 10);
+        if (*endptr != '\0') {
+            D(WARN, "Value for %s is not a number: %s", name, tmp);
+            return 1;
+        }
+        *ptr = val;
+        return 0;
+    }
+    return 1;
+}
+
+static int get_config_string(cnode *node, char *ptr, int max_len, const char *name)
+{
+    size_t begin, end;
+    const char *value = config_str(node, name, NULL);
+    if (!value)
+        return -1;
+
+    begin = strcspn(value, "\"") + 1;
+    end = strcspn(&value[begin], "\"");
+
+    if ((int) end > max_len) {
+        D(WARN, "Identifier \"%s\" too long", value);
+        return -1;
+    }
+
+    strncpy(ptr, &value[begin], end);
+    if((int) end < max_len)
+        ptr[end] = 0;
+    return 0;
+}
+
+static void GPT_parse_header(cnode *node, struct GPT_content *content)
+{
+    get_config_uint64(node, &content->header.current_lba, "header_lba");
+    get_config_uint64(node, &content->header.backup_lba, "backup_lba");
+    get_config_uint64(node, &content->header.first_usable_lba, "first_lba");
+    get_config_uint64(node, &content->header.last_usable_lba, "last_lba");
+    get_config_uint64(node, &content->header.entries_lba, "entries_lba");
+    get_config_string(node, (char *) content->header.disk_guid, 16, "guid");
+}
+
+static int GPT_parse_partitions(cnode *node, struct GPT_content *content)
+{
+    cnode *current;
+    int i;
+    uint64_t partition_size;
+    struct GPT_entry_raw *entry;
+    for (i = 0, current = node->first_child; current; current = current->next, ++i) {
+        entry = &content->entries[i];
+        entry_set_guid(i, content->entries[i].partition_guid);
+        memcpy(&content->entries[i].type_guid, partition_type_uuid, 16);
+        if (get_config_uint64(current, &entry->first_lba, "first_lba")) {
+            D(ERR, "first_lba not specified");
+            return 1;
+        }
+        if (get_config_uint64(current, &partition_size, "partition_size")) {
+            D(ERR, "partition_size not specified");
+            return 1;
+        }
+        if (config_str(current, "system", NULL)) {
+            entry->flags |= GPT_FLAG_SYSTEM;
+        }
+        if (config_str(current, "bootable", NULL)) {
+            entry->flags |= GPT_FLAG_BOOTABLE;
+        }
+        if (config_str(current, "readonly", NULL)) {
+            entry->flags |= GPT_FLAG_READONLY;
+        }
+        if (config_str(current, "automount", NULL)) {
+            entry->flags |= GPT_FLAG_DOAUTOMOUNT;
+        }
+
+        get_config_uint64(current, &content->entries[i].flags, "flags");
+        content->entries[i].last_lba = content->entries[i].first_lba + partition_size - 1;
+        GPT_to_UTF16(content->entries[i].name, current->name, 16);
+    }
+    return 0;
+}
+
+static inline int cnode_count(cnode *node)
+{
+    int i;
+    cnode *current;
+    for (i = 0, current = node->first_child; current; current = current->next, ++i)
+        ;
+    return i;
+}
+
+
+static int GPT_parse_cnode(cnode *root, struct GPT_content *content)
+{
+    cnode *partnode;
+
+    if (!(partnode = config_find(root, "partitions"))) {
+        D(ERR, "Could not find partition table");
+        return 0;
+    }
+
+    GPT_parse_header(root, content);
+
+    content->header.entries_count = cnode_count(partnode);
+    content->entries = malloc(content->header.entries_count * sizeof(struct GPT_entry_raw));
+
+    if (GPT_parse_partitions(partnode, content)) {
+        D(ERR, "Could not parse partitions");
+        return 0;
+    }
+
+    return 1;
+}
+
+int GPT_parse_file(int fd, struct GPT_content *content)
+{
+    char *data;
+    int size;
+    int ret;
+    cnode *root = config_node("", "");
+
+    size = get_file_size(fd);
+    data = (char *) mmap(NULL, size + 1, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
+
+    if (data == NULL) {
+        if (size == 0)
+            D(ERR, "config file empty");
+        else
+            D(ERR, "Out of memory");
+        return 0;
+    }
+
+    data[size - 1] = 0;
+    config_load(root, data);
+
+    if (root->first_child == NULL) {
+        D(ERR, "Could not read config file");
+        return 0;
+    }
+
+    ret = GPT_parse_cnode(root, content);
+    munmap(data, size);
+    return ret;
+}
+
+void GPT_release_content(struct GPT_content *content)
+{
+    free(content->entries);
+}
+
+int GPT_write_content(const char *device, struct GPT_content *content)
+{
+    struct GPT_entry_table *maptable;
+
+    maptable = GPT_get_from_content(device, content);
+    if (maptable == NULL) {
+        D(ERR, "could not map device");
+        return 0;
+    }
+
+    memcpy(maptable->header, &content->header, sizeof(*maptable->header));
+    memcpy(maptable->entries, content->entries,
+           content->header.entries_count * sizeof(*maptable->entries));
+
+    GPT_sync(maptable);
+    GPT_release_device(maptable);
+
+    return 1;
+}
+
diff --git a/fastbootd/commands/partitions.h b/fastbootd/commands/partitions.h
new file mode 100644
index 0000000..9a6a88d
--- /dev/null
+++ b/fastbootd/commands/partitions.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef __FASTBOOTD_PATITIONS_
+#define __FASTBOOTD_PATITIONS_
+
+#include <stdint.h>
+
+#define GPT_ENTRIES 128
+#define GPT_NAMELEN 36
+
+#define GPT_FLAG_SYSTEM (1ULL << 0)
+#define GPT_FLAG_BOOTABLE (1ULL << 2)
+#define GPT_FLAG_READONLY (1ULL << 60)
+#define GPT_FLAG_DOAUTOMOUNT (1ULL << 63)
+
+// it should be passed in little endian order
+struct GPT_entry_raw {
+    uint8_t type_guid[16];
+    uint8_t partition_guid[16];
+    uint64_t first_lba; // little endian
+    uint64_t last_lba;
+    uint64_t flags;
+    uint16_t name[GPT_NAMELEN]; // UTF-16 LE
+};
+
+struct GPT_mapping {
+    void *map_ptr;
+    void *ptr;
+    unsigned size;
+};
+
+struct GPT_entry_table {
+    int fd;
+
+    struct GPT_mapping header_map;
+    struct GPT_mapping entries_map;
+    struct GPT_mapping sec_header_map;
+    struct GPT_mapping sec_entries_map;
+
+    struct GPT_header *header;
+    struct GPT_entry_raw *entries;
+    struct GPT_header *second_header;
+    struct GPT_entry_raw *second_entries;
+
+    unsigned sector_size;
+    unsigned partition_table_size;
+    int second_valid;
+};
+
+struct GPT_header {
+    uint8_t signature[8];
+    uint32_t revision;
+    uint32_t header_size;
+    uint32_t header_checksum;
+    uint32_t reserved_zeros;
+    uint64_t current_lba;
+    uint64_t backup_lba;
+    uint64_t first_usable_lba;
+    uint64_t last_usable_lba;
+    uint8_t disk_guid[16];
+    uint64_t entries_lba;
+    uint32_t entries_count;
+    uint32_t entry_size;
+    uint32_t partition_array_checksum;
+    // the rest should be filled with zeros
+} __attribute__((packed));
+
+struct GPT_content {
+    struct GPT_header header;
+    struct GPT_entry_raw *entries;
+};
+
+
+struct GPT_entry_table* GPT_get_device(const char *, unsigned lba);
+
+void GPT_release_device(struct GPT_entry_table *);
+
+void GPT_edit_entry(struct GPT_entry_table *table,
+                    struct GPT_entry_raw *old_entry,
+                    struct GPT_entry_raw *new_entry);
+
+int GPT_delete_entry(struct GPT_entry_table *table, struct GPT_entry_raw *entry);
+
+void GPT_add_entry(struct GPT_entry_table *table, struct GPT_entry_raw *entry);
+
+struct GPT_entry_raw *GPT_get_pointer(struct GPT_entry_table *table, struct GPT_entry_raw *entry);
+struct GPT_entry_raw *GPT_get_pointer_by_guid(struct GPT_entry_table *, const char *);
+struct GPT_entry_raw *GPT_get_pointer_by_name(struct GPT_entry_table *, const char *);
+
+//Use after every edit operation
+void GPT_sync();
+
+void GPT_to_UTF16(uint16_t *, const char *, int );
+void GPT_from_UTF16(char *, const uint16_t *, int);
+
+int GPT_parse_entry(char *string, struct GPT_entry_raw *entry);
+
+void GPT_default_content(struct GPT_content *content, struct GPT_entry_table *table);
+
+void GPT_release_content(struct GPT_content *content);
+
+int GPT_parse_file(int fd, struct GPT_content *content);
+
+int GPT_write_content(const char *device, struct GPT_content *content);
+
+int gpt_mmap(struct GPT_mapping *mapping, uint64_t location, int size, int fd);
+
+void gpt_unmap(struct GPT_mapping *mapping);
+
+#endif
diff --git a/fastbootd/commands/virtual_partitions.c b/fastbootd/commands/virtual_partitions.c
new file mode 100644
index 0000000..813f485
--- /dev/null
+++ b/fastbootd/commands/virtual_partitions.c
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "commands/virtual_partitions.h"
+#include "debug.h"
+
+static struct virtual_partition *partitions = NULL;
+
+int try_handle_virtual_partition(struct protocol_handle *handle, const char *arg)
+{
+    struct virtual_partition *current;
+
+    for (current = partitions; current != NULL; current = current->next) {
+        if (!strcmp(current->name, arg)) {
+            current->handler(handle, arg);
+        }
+    }
+
+    return 0;
+}
+
+void virtual_partition_register(
+        const char * name,
+        void (*handler)(struct protocol_handle *phandle, const char *arg))
+{
+    struct virtual_partition *new;
+    new = malloc(sizeof(*new));
+    if (new) {
+        new->name = name;
+        new->handler = handler;
+        new->next = partitions;
+        partitions = new;
+    }
+    else {
+        D(ERR, "Out of memory");
+    }
+}
diff --git a/fastbootd/commands/virtual_partitions.h b/fastbootd/commands/virtual_partitions.h
new file mode 100644
index 0000000..88df71e
--- /dev/null
+++ b/fastbootd/commands/virtual_partitions.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef FASTBOOTD_VIRTUAL_PARTITIONS_H
+#define FASTBOOTD_VIRTUAL_PARTITIONS_H
+
+#include "protocol.h"
+
+struct virtual_partition {
+    struct virtual_partition *next;
+    const char *name;
+    void (*handler)(struct protocol_handle *phandle, const char *arg);
+};
+
+int try_handle_virtual_partition(struct protocol_handle *handle, const char *arg);
+
+void virtual_partition_register(
+        const char * name,
+        void (*handler)(struct protocol_handle *phandle, const char *arg));
+
+#endif
diff --git a/fastbootd/config.c b/fastbootd/config.c
index b8503fd..fe6da69 100644
--- a/fastbootd/config.c
+++ b/fastbootd/config.c
@@ -35,29 +35,13 @@
 #include <sys/types.h>
 
 #include "protocol.h"
+#include "utils.h"
 
 #include "debug.h"
 
 // TODO: change config path
 #define CONFIG_PATH "/data/fastboot.cfg"
 
-static char *strip(char *str)
-{
-    int n;
-
-    n = strspn(str, " \t");
-    str += n;
-
-    for (n = strlen(str) - 1; n >= 0; n--) {
-        if (str[n] == ' ' || str[n] == '\t')
-            str[n] = '\0';
-        else
-            break;
-    }
-
-    return str;
-}
-
 static int config_parse_line(char *line)
 {
     char *c;
diff --git a/fastbootd/fastbootd.c b/fastbootd/fastbootd.c
index 98df0db..2b51b33 100644
--- a/fastbootd/fastbootd.c
+++ b/fastbootd/fastbootd.c
@@ -16,30 +16,85 @@
 
 #include <stdio.h>
 #include <unistd.h>
-
 #include <cutils/klog.h>
+#include <getopt.h>
+#include <stdlib.h>
 
 #include "debug.h"
+#include "trigger.h"
+#include "socket_client.h"
+#include "secure.h"
 
 unsigned int debug_level = DEBUG;
 
 void commands_init();
 void usb_init();
 void config_init();
+int transport_socket_init();
+int network_discovery_init();
+void ssh_server_start();
 
 int main(int argc, char **argv)
 {
+    int socket_client = 0;
+    int c;
+    int network = 1;
+
+    klog_init();
+    klog_set_level(6);
+
+    const struct option longopts[] = {
+        {"socket", no_argument, 0, 'S'},
+        {"nonetwork", no_argument, 0, 'n'},
+        {0, 0, 0, 0}
+    };
+
+    while (1) {
+        c = getopt_long(argc, argv, "Sn", longopts, NULL);
+        /* Alphabetical cases */
+        if (c < 0)
+            break;
+        switch (c) {
+        case 'S':
+            socket_client = 1;
+            break;
+        case 'n':
+            network = 0;
+            break;
+        case '?':
+            return 1;
+        default:
+            return 0;
+        }
+    }
+
     (void)argc;
     (void)argv;
 
     klog_init();
     klog_set_level(6);
 
-    config_init();
-    commands_init();
-    usb_init();
-    while (1) {
-        sleep(1);
+    if (socket_client) {
+        //TODO: Shouldn't we change current tty into raw mode?
+        run_socket_client();
+    }
+    else {
+        cert_init_crypto();
+        config_init();
+        load_trigger();
+        commands_init();
+        usb_init();
+
+        if (network) {
+            if (!transport_socket_init())
+                exit(1);
+            ssh_server_start();
+            network_discovery_init();
+        }
+
+        while (1) {
+            sleep(1);
+        }
     }
     return 0;
 }
diff --git a/fastbootd/include/vendor_trigger.h b/fastbootd/include/vendor_trigger.h
new file mode 100644
index 0000000..51204fa
--- /dev/null
+++ b/fastbootd/include/vendor_trigger.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef __VENDOR_TRIGGER_H_
+#define __VENDOR_TRIGGER_H_
+
+#define TRIGGER_MODULE_ID "fastbootd"
+#include <hardware/hardware.h>
+
+__BEGIN_DECLS
+
+struct GPT_entry_raw;
+struct GPT_content;
+
+/*
+ * Structer with function pointers may become longer in the future
+ */
+
+struct vendor_trigger_t {
+    struct hw_device_t common;
+
+    /*
+     * This function runs at the beggining and shoud never be changed
+     *
+     * version is number parameter indicating version on the fastbootd side
+     * libversion is version indicateing version of the library version
+     *
+     * returns 0 if it can cooperate with the current version and 1 in opposite
+     */
+    int (*check_version)(const int version, int *libversion);
+
+
+    /*
+     * Return value -1 forbid the action from the vendor site and sets errno
+     */
+    int (* gpt_layout)(struct GPT_content *);
+    int (* oem_cmd)(const char *arg, const char **response);
+};
+
+__END_DECLS
+
+#endif
diff --git a/fastbootd/network_discovery.c b/fastbootd/network_discovery.c
new file mode 100644
index 0000000..1cd3e48
--- /dev/null
+++ b/fastbootd/network_discovery.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <dns_sd.h>
+#include <cutils/properties.h>
+#include <unistd.h>
+
+#include "debug.h"
+#include "network_discovery.h"
+#include "utils.h"
+
+#define MDNS_SERVICE_NAME "mdnsd"
+#define MDNS_SERVICE_STATUS "init.svc.mdnsd"
+#define FASTBOOTD_TYPE "_fastbootd._tcp"
+#define FASTBOOTD_DOMAIN "local."
+#define FASTBOOTD_NAME "fastbootd"
+
+
+static void reg_reply(DNSServiceRef sdref, const DNSServiceFlags flags, DNSServiceErrorType errorCode,
+    const char *name, const char *regtype, const char *domain, void *context)
+{
+    (void)sdref;    // Unused
+    (void)flags;    // Unused
+    (void)context;  // Unused
+    if (errorCode == kDNSServiceErr_ServiceNotRunning) {
+        fprintf(stderr, "Error code %d\n", errorCode);
+    }
+
+
+    printf("Got a reply for service %s.%s%s: ", name, regtype, domain);
+
+    if (errorCode == kDNSServiceErr_NoError)
+    {
+        if (flags & kDNSServiceFlagsAdd)
+            printf("Name now registered and active\n");
+        else
+            printf("Name registration removed\n");
+        if (errorCode == kDNSServiceErr_NameConflict)
+            printf("Name in use, please choose another\n");
+        else
+            printf("Error %d\n", errorCode);
+
+        if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout);
+    }
+}
+
+static int register_service() {
+    DNSServiceRef sdref = NULL;
+    const char *domain = FASTBOOTD_DOMAIN;
+    const char *type = FASTBOOTD_TYPE;
+    const char *host = NULL;
+    char name[PROP_VALUE_MAX];
+    uint16_t port = 22;
+    int flags = 0;
+    DNSServiceErrorType result;
+    property_get("ro.serialno", name, "");
+    if (!strcmp(name, "")) {
+        D(ERR, "No property serialno");
+        return -1;
+    }
+
+    result = DNSServiceRegister(&sdref, flags, kDNSServiceInterfaceIndexAny,
+                       name, type, domain, host, port,
+                       0, NULL, reg_reply, NULL);
+    if (result != kDNSServiceErr_NoError) {
+        D(ERR, "Unable to register service");
+        return -1;
+    }
+    return 0;
+}
+
+
+int network_discovery_init()
+{
+    D(INFO, "Starting discovery");
+    if (service_start(MDNS_SERVICE_NAME)) {
+        D(ERR, "Unable to start discovery");
+        return -1;
+    }
+
+    if (register_service()) {
+        D(ERR, "Unable to register service");
+        return -1;
+    }
+
+    return 0;
+}
+
diff --git a/fastbootd/network_discovery.h b/fastbootd/network_discovery.h
new file mode 100644
index 0000000..75fda63
--- /dev/null
+++ b/fastbootd/network_discovery.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _FASTBOOTD_NETWORK_DISCOVERY_H
+#define _FASTBOOTD_NETWORK_DISCOVERY_H
+
+int network_discovery_init();
+
+#endif
diff --git a/fastbootd/other/gptedit.c b/fastbootd/other/gptedit.c
new file mode 100644
index 0000000..16d34a5
--- /dev/null
+++ b/fastbootd/other/gptedit.c
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <unistd.h>
+
+#include <cutils/klog.h>
+
+#include "commands/partitions.h"
+#include "debug.h"
+
+unsigned int debug_level = DEBUG;
+//TODO: add tool to generate config file
+
+void usage() {
+    fprintf(stderr,
+            "usage: test_gpt [ <option> ] <file>\n"
+            "\n"
+            "options:\n"
+            "  -p                                       print partitions\n"
+            "  -c                                       print config file\n"
+            "  -a                                       adds new partition\n"
+            "  -d                                       deletes partition (-o needed)\n"
+            "\n"
+            "  -n name@startlba,endlba                  new partition detail\n"
+            "  -o                                       old partition name\n"
+            "  -t                                       type guid\n"
+            "  -g                                       partition guid\n"
+            "  -l gpt_location                          specyfies gpt secto\n"
+    );
+
+}
+
+void printGPT(struct GPT_entry_table *table);
+void addGPT(struct GPT_entry_table *table, const char *arg, const char *guid, const char *tguid);
+void deleteGPT(struct GPT_entry_table *table, const char *name);
+void configPrintGPT(struct GPT_entry_table *table);
+
+int main(int argc, char *argv[]) {
+    int print_cmd = 0;
+    int config_cmd = 0;
+    int add_cmd = 0;
+    int del_cmd = 0;
+    int sync_cmd = 0;
+    int c;
+    const char *new_partition = NULL;
+    const char *old_partition = NULL;
+    const char *type_guid = NULL;
+    const char *partition_guid = NULL;
+    unsigned gpt_location = 1;
+
+    klog_init();
+    klog_set_level(6);
+
+    const struct option longopts[] = {
+        {"print", no_argument, 0, 'p'},
+        {"config-print", no_argument, 0, 'c'},
+        {"add", no_argument, 0, 'a'},
+        {"del", no_argument, 0, 'd'},
+        {"new", required_argument, 0, 'n'},
+        {"old", required_argument, 0, 'o'},
+        {"type", required_argument, 0, 't'},
+        {"sync", required_argument, 0, 's'},
+        {"guid", required_argument, 0, 'g'},
+        {"location", required_argument, 0, 'l'},
+        {0, 0, 0, 0}
+    };
+
+    while (1) {
+        c = getopt_long(argc, argv, "pcadt:g:n:o:sl:", longopts, NULL);
+        /* Alphabetical cases */
+        if (c < 0)
+            break;
+        switch (c) {
+        case 'p':
+            print_cmd = 1;
+            break;
+        case 'c':
+            config_cmd = 1;
+            break;
+        case 'a':
+            add_cmd = 1;
+            break;
+        case 'd':
+            del_cmd = 1;
+            break;
+        case 'n':
+            new_partition = optarg;
+            break;
+        case 'o':
+            old_partition = optarg;
+            break;
+        case 't':
+            type_guid = optarg;
+        case 'g':
+            partition_guid = optarg;
+            break;
+        case 's':
+            sync_cmd = 1;
+            break;
+        case 'l':
+            gpt_location = strtoul(optarg, NULL, 10);
+            fprintf(stderr, "Got offset as %d", gpt_location);
+            break;
+        case '?':
+            return 1;
+        default:
+            abort();
+        }
+    }
+
+    argc -= optind;
+    argv += optind;
+
+    if (argc < 1) {
+        usage();
+        return 1;
+    }
+
+    const char *path = argv[0];
+    struct GPT_entry_table *table = GPT_get_device(path, gpt_location);
+    if (table == NULL) {
+        fprintf(stderr, "unable to get GPT table from %s\n", path);
+        return 1;
+    }
+
+    if (add_cmd)
+        addGPT(table, new_partition, partition_guid, type_guid);
+    if (del_cmd)
+        deleteGPT(table, old_partition);
+    if (print_cmd)
+        printGPT(table);
+    if (config_cmd)
+        configPrintGPT(table);
+    if (sync_cmd)
+        GPT_sync(table);
+
+    GPT_release_device(table);
+
+    return 0;
+}
+
+void printGPT(struct GPT_entry_table *table) {
+    struct GPT_entry_raw *entry = table->entries;
+    unsigned n, m;
+    char name[GPT_NAMELEN + 1];
+
+    printf("ptn  start block   end block     name\n");
+    printf("---- ------------- -------------\n");
+
+    for (n = 0; n < table->header->entries_count; n++, entry++) {
+        if (entry->type_guid[0] == 0)
+            continue;
+        for (m = 0; m < GPT_NAMELEN; m++) {
+            name[m] = entry->name[m] & 127;
+        }
+        name[m] = 0;
+        printf("#%03d %13lld %13lld %s\n",
+            n + 1, entry->first_lba, entry->last_lba, name);
+    }
+}
+
+void configPrintGPT(struct GPT_entry_table *table) {
+    struct GPT_entry_raw *entry = table->entries;
+    unsigned n, m;
+    char name[GPT_NAMELEN + 1];
+    char temp_guid[17];
+    temp_guid[16] = 0;
+
+    printf("header_lba %lld\n", table->header->current_lba);
+    printf("backup_lba %lld\n", table->header->backup_lba);
+    printf("first_lba %lld\n", table->header->first_usable_lba);
+    printf("last_lba %lld\n", table->header->last_usable_lba);
+    printf("entries_lba %lld\n", table->header->entries_lba);
+    snprintf(temp_guid, 17, "%s", table->header->disk_guid);
+    printf("guid \"%s\"", temp_guid);
+
+    printf("\npartitions {\n");
+
+    for (n = 0; n < table->header->entries_count; n++, entry++) {
+        uint64_t size = entry->last_lba - entry->first_lba + 1;
+
+        if (entry->type_guid[0] == 0)
+            continue;
+        for (m = 0; m < GPT_NAMELEN; m++) {
+            name[m] = entry->name[m] & 127;
+        }
+        name[m] = 0;
+
+        printf("    %s {\n", name);
+        snprintf(temp_guid, 17, "%s", entry->partition_guid);
+        printf("        guid \"%s\"\n", temp_guid);
+        printf("        first_lba %lld\n", entry->first_lba);
+        printf("        partition_size %lld\n", size);
+        if (entry->flags & GPT_FLAG_SYSTEM)
+            printf("        system\n");
+        if (entry->flags & GPT_FLAG_BOOTABLE)
+            printf("        bootable\n");
+        if (entry->flags & GPT_FLAG_READONLY)
+            printf("        readonly\n");
+        if (entry->flags & GPT_FLAG_DOAUTOMOUNT)
+            printf("        automount\n");
+        printf("    }\n\n");
+    }
+    printf("}\n");
+}
+
+void addGPT(struct GPT_entry_table *table, const char *str  , const char *guid, const char *tguid) {
+    char *c, *c2;
+    char *arg = malloc(strlen(str));
+    char *name = arg;
+    unsigned start, end;
+    strcpy(arg, str);
+    if (guid == NULL || tguid == NULL) {
+        fprintf(stderr, "Type guid and partion guid needed");
+        free(arg);
+        return;
+    }
+
+    c = strchr(arg, '@');
+
+    if (c == NULL) {
+        fprintf(stderr, "Wrong entry format");
+        free(arg);
+        return;
+    }
+
+    *c++ = '\0';
+
+    c2 = strchr(c, ',');
+
+    if (c2 == NULL) {
+        fprintf(stderr, "Wrong entry format");
+        free(arg);
+        return;
+    }
+
+    start = strtoul(c, NULL, 10);
+    *c2++ = '\0';
+    end = strtoul(c2, NULL, 10);
+
+    struct GPT_entry_raw data;
+    strncpy((char *)data.partition_guid, guid, 15);
+    data.partition_guid[15] = '\0';
+    strncpy((char *)data.type_guid, tguid, 15);
+    data.type_guid[15] = '\0';
+    GPT_to_UTF16(data.name, name, GPT_NAMELEN);
+    data.first_lba = start;
+    data.last_lba = end;
+
+    fprintf(stderr, "Adding (%d,%d) %s as, [%s, %s]", start, end, name, (char *) data.type_guid, (char *) data.partition_guid);
+    GPT_add_entry(table, &data);
+    free(arg);
+}
+
+void deleteGPT(struct GPT_entry_table *table, const char *name) {
+    struct GPT_entry_raw *entry;
+
+    if (name == NULL) {
+        fprintf(stderr, "Need partition name");
+        return;
+    }
+
+    entry = GPT_get_pointer_by_name(table, name);
+
+    if (!entry) {
+        fprintf(stderr, "Unable to find partition: %s", name);
+        return;
+    }
+    GPT_delete_entry(table, entry);
+}
+
diff --git a/fastbootd/other/partitions.sample.cfg b/fastbootd/other/partitions.sample.cfg
new file mode 100644
index 0000000..49562cf
--- /dev/null
+++ b/fastbootd/other/partitions.sample.cfg
@@ -0,0 +1,60 @@
+
+header_lba 1
+backup_lba 101
+first_lba 43
+last_lba 100
+entries_lba 2
+
+partitions {
+    #You can generate this as output from gptedit -c
+    SOS {
+        first_lba 28672
+        partition_size 16384
+    }
+
+    DTB {
+        first_lba 45056
+        partition_size 8192
+    }
+
+    LNX {
+        first_lba 53248
+        partition_size 16384
+    }
+
+    APP {
+        first_lba 69632
+        partition_size 1048576
+    }
+
+    CAC {
+        first_lba 1118208
+        partition_size 1572864
+    }
+
+    MSC {
+        first_lba 2691072
+        partition_size 4096
+    }
+
+    USP {
+        first_lba 2695168
+        partition_size 65536
+    }
+
+    MDA {
+        first_lba 2760704
+        partition_size 4096
+    }
+
+    FCT {
+        first_lba 2764800
+        partition_size 32768
+    }
+
+    UDA {
+        first_lba 2797568
+        partition_size 27975680
+    }
+}
+
diff --git a/fastbootd/other/sign/src/SignImg.java b/fastbootd/other/sign/src/SignImg.java
new file mode 100644
index 0000000..338d427
--- /dev/null
+++ b/fastbootd/other/sign/src/SignImg.java
@@ -0,0 +1,181 @@
+package signtool;
+
+import java.io.*;
+import java.util.Properties;
+import java.util.ArrayList;
+
+import javax.mail.internet.*;
+import javax.mail.MessagingException;
+import javax.mail.Session;
+import javax.activation.MailcapCommandMap;
+import javax.activation.CommandMap;
+
+import java.security.PrivateKey;
+import java.security.Security;
+import java.security.KeyFactory;
+import java.security.KeyStore;
+import java.security.NoSuchAlgorithmException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.cert.X509Certificate;
+import java.security.cert.CertificateFactory;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateEncodingException;
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
+import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+import org.bouncycastle.cms.CMSProcessableByteArray;
+import org.bouncycastle.cms.CMSSignedGenerator;
+import org.bouncycastle.cms.CMSSignedDataGenerator;
+import org.bouncycastle.cms.CMSSignedGenerator;
+import org.bouncycastle.cms.CMSProcessable;
+import org.bouncycastle.cms.CMSSignedData;
+import org.bouncycastle.cms.CMSTypedData;
+import org.bouncycastle.cert.jcajce.JcaCertStore;
+import org.bouncycastle.util.Store;
+import org.bouncycastle.asn1.ASN1InputStream;    
+import org.bouncycastle.asn1.DEROutputStream;
+import org.bouncycastle.asn1.ASN1Object;
+
+
+public class SignImg {
+
+    /* It reads private key in pkcs#8 formate
+     * Conversion:
+     * openssl pkcs8 -topk8 -nocrypt -outform DER < inkey.pem > outkey.pk8
+     */
+    private static PrivateKey getPrivateKey(String path) throws IOException, FileNotFoundException, NoSuchAlgorithmException, InvalidKeySpecException {
+        File file = new File(path);
+        FileInputStream fis = new FileInputStream(file);
+        byte[] data = new byte[(int)file.length()];
+        fis.read(data);
+        fis.close();
+
+        PKCS8EncodedKeySpec kspec = new PKCS8EncodedKeySpec(data);
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        PrivateKey privateKey = kf.generatePrivate(kspec);
+
+        return privateKey;
+    }
+
+    private static MimeBodyPart getContent(String path) throws IOException, FileNotFoundException, MessagingException {
+        MimeBodyPart body = new MimeBodyPart();
+
+        File file = new File(path);
+        FileInputStream fis = new FileInputStream(file);
+        byte[] data = new byte[(int)file.length()];
+        fis.read(data);
+        fis.close();
+
+        body.setContent(data, "application/octet-stream");
+
+        return body;
+    }
+
+    private static CMSProcessableByteArray getCMSContent(String path) throws IOException, FileNotFoundException, MessagingException {
+        File file = new File(path);
+        FileInputStream fis = new FileInputStream(file);
+        byte[] data = new byte[(int)file.length()];
+        fis.read(data);
+        fis.close();
+        CMSProcessableByteArray cms = new CMSProcessableByteArray(data);
+
+        return cms;
+    }
+
+    private static X509Certificate readCert(String path) throws IOException, FileNotFoundException, CertificateException {
+        File file = new File(path);
+        FileInputStream is = new FileInputStream(file);
+
+        CertificateFactory cf = CertificateFactory.getInstance("X.509");
+        Certificate cert = cf.generateCertificate(is);
+        is.close();
+
+        return (X509Certificate) cert;
+    }
+
+    private static void save(MimeBodyPart content, String path) throws IOException, FileNotFoundException, MessagingException {
+        File file = new File(path);
+        FileOutputStream os = new FileOutputStream(file);
+
+        content.writeTo(os);
+
+        os.close();
+    }
+
+    private static Store certToStore(X509Certificate certificate) throws CertificateEncodingException {
+        ArrayList<X509Certificate> certList = new ArrayList<X509Certificate>();
+        certList.add(certificate);
+        return new JcaCertStore(certList);
+    }
+
+    public static void setDefaultMailcap()
+    {
+        MailcapCommandMap _mailcap =
+            (MailcapCommandMap)CommandMap.getDefaultCommandMap();
+
+        _mailcap.addMailcap("application/pkcs7-signature;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.pkcs7_signature");
+        _mailcap.addMailcap("application/pkcs7-mime;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.pkcs7_mime");
+        _mailcap.addMailcap("application/x-pkcs7-signature;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.x_pkcs7_signature");
+        _mailcap.addMailcap("application/x-pkcs7-mime;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.x_pkcs7_mime");
+        _mailcap.addMailcap("multipart/signed;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.multipart_signed");
+
+        CommandMap.setDefaultCommandMap(_mailcap);
+    } 
+
+    public static void main(String[] args) {
+        try {
+            if (args.length < 4) {
+                System.out.println("Usage: signimg data private_key certificate output");
+                return;
+            }
+            System.out.println("Signing the image");
+            setDefaultMailcap();
+
+            Security.addProvider(new BouncyCastleProvider());
+
+            PrivateKey key = getPrivateKey(args[1]);
+            System.out.println("File read sucessfully");
+
+            CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
+
+            CMSTypedData body = getCMSContent(args[0]);
+            System.out.println("Content read sucessfully");
+
+            X509Certificate cert = (X509Certificate) readCert(args[2]);
+            System.out.println("Certificate read sucessfully");
+
+            ContentSigner sha256Signer = new JcaContentSignerBuilder("SHA256withRSA").setProvider("BC").build(key);
+
+            Store certs = certToStore(cert);
+
+            generator.addCertificates(certs);
+            generator.addSignerInfoGenerator(
+                          new JcaSignerInfoGeneratorBuilder(
+                                new JcaDigestCalculatorProviderBuilder().setProvider("BC").build())
+                          .build(sha256Signer, cert));
+
+            CMSSignedData signed = generator.generate(body, true);
+            System.out.println("Signed");
+
+            Properties props = System.getProperties();
+            Session session = Session.getDefaultInstance(props, null);
+            
+            File file = new File(args[3]);
+            FileOutputStream os = new FileOutputStream(file);
+
+            ASN1InputStream asn1 = new ASN1InputStream(signed.getEncoded());
+            ByteArrayOutputStream out = new ByteArrayOutputStream(); 
+            DEROutputStream dOut = new DEROutputStream(os); 
+            dOut.writeObject(ASN1Object.fromByteArray(signed.getEncoded()));
+
+        }
+        catch (Exception ex) {
+            System.out.println("Exception during programm execution: " + ex.getMessage());
+        }
+    }
+}
diff --git a/fastbootd/other/vendor_trigger.c b/fastbootd/other/vendor_trigger.c
new file mode 100644
index 0000000..101959b
--- /dev/null
+++ b/fastbootd/other/vendor_trigger.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+
+#include "vendor_trigger.h"
+#include "debug.h"
+
+unsigned int debug_level = DEBUG;
+
+static const int version = 1;
+
+int check_version(const int fastboot_version, int *libversion) {
+    *libversion = version;
+    return !(fastboot_version == version);
+}
+
+int gpt_layout(struct GPT_content *table) {
+    D(DEBUG, "message from libvendor");
+    return 0;
+}
+
+int oem_cmd(const char *arg, const char **response) {
+    D(DEBUG, "message from libvendor, oem catched request %s", arg);
+    return 0;
+}
+
+static int close_triggers(struct vendor_trigger_t *dev)
+{
+    if (dev)
+        free(dev);
+
+    return 0;
+}
+
+static int open_triggers(const struct hw_module_t *module, char const *name,
+                         struct hw_device_t **device) {
+    struct vendor_trigger_t *dev = malloc(sizeof(struct vendor_trigger_t));
+    klog_init();
+    klog_set_level(6);
+
+    memset(dev, 0, sizeof(*dev));
+    dev->common.module = (struct hw_module_t *) module;
+    dev->common.close  = (int (*)(struct hw_device_t *)) close_triggers;
+
+    dev->gpt_layout = gpt_layout;
+    dev->oem_cmd = oem_cmd;
+
+    *device = (struct hw_device_t *) dev;
+
+    return 0;
+}
+
+
+static struct hw_module_methods_t trigger_module_methods = {
+    .open = open_triggers,
+};
+
+struct hw_module_t HAL_MODULE_INFO_SYM = {
+    .tag = HARDWARE_MODULE_TAG,
+    .version_major = 1,
+    .version_minor = 0,
+    .id = TRIGGER_MODULE_ID,
+    .name = "vendor trigger library for fastbootd",
+    .author = "Google, Inc.",
+    .methods = &trigger_module_methods,
+};
+
diff --git a/fastbootd/secure.c b/fastbootd/secure.c
new file mode 100644
index 0000000..a657ad4
--- /dev/null
+++ b/fastbootd/secure.c
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include <openssl/pem.h>
+#include <openssl/engine.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+#include <openssl/pem.h>
+#include <openssl/cms.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "secure.h"
+#include "debug.h"
+#include "utils.h"
+
+
+void cert_init_crypto() {
+    CRYPTO_malloc_init();
+    ERR_load_crypto_strings();
+    OpenSSL_add_all_algorithms();
+    ENGINE_load_builtin_engines();
+}
+
+X509_STORE *cert_store_from_path(const char *path) {
+
+    X509_STORE *store;
+    struct stat st;
+    X509_LOOKUP *lookup;
+
+    if (stat(path, &st)) {
+        D(ERR, "Unable to stat cert path");
+        goto error;
+    }
+
+    if (!(store = X509_STORE_new())) {
+        goto error;
+    }
+
+    if (S_ISDIR(st.st_mode)) {
+        lookup = X509_STORE_add_lookup(store,X509_LOOKUP_hash_dir());
+        if (lookup == NULL)
+            goto error;
+        if (!X509_LOOKUP_add_dir(lookup, path, X509_FILETYPE_PEM)) {
+            D(ERR, "Error loading cert directory %s", path);
+            goto error;
+        }
+    }
+    else if(S_ISREG(st.st_mode)) {
+        lookup = X509_STORE_add_lookup(store,X509_LOOKUP_file());
+        if (lookup == NULL)
+            goto error;
+        if (!X509_LOOKUP_load_file(lookup, path, X509_FILETYPE_PEM)) {
+            D(ERR, "Error loading cert directory %s", path);
+            goto error;
+        }
+    }
+    else {
+        D(ERR, "cert path is not directory or regular file");
+        goto error;
+    }
+
+    return store;
+
+error:
+    return NULL;
+}
+
+
+int cert_read(int fd, CMS_ContentInfo **content, BIO **output) {
+    BIO *input;
+    *output = NULL;
+
+
+    input = BIO_new_fd(fd, BIO_NOCLOSE);
+    if (input == NULL) {
+        D(ERR, "Unable to open input");
+        goto error;
+    }
+
+    //TODO:
+    // read with d2i_CMS_bio to support DER
+    // with java or just encode data with base64
+    *content = SMIME_read_CMS(input, output);
+    if (*content == NULL) {
+        unsigned long err = ERR_peek_last_error();
+        D(ERR, "Unable to parse input file: %s", ERR_lib_error_string(err));
+        goto error_read;
+    }
+
+    BIO_free(input);
+
+    return 0;
+
+error_read:
+    BIO_free(input);
+error:
+    return 1;
+}
+
+int cert_verify(BIO *content, CMS_ContentInfo *content_info, X509_STORE *store, int *out_fd) {
+    BIO *output_temp;
+    int ret;
+
+    *out_fd = create_temp_file();
+    if (*out_fd < 0) {
+        D(ERR, "unable to create temporary file");
+        return -1;
+    }
+
+    output_temp = BIO_new_fd(*out_fd, BIO_NOCLOSE);
+    if (output_temp == NULL) {
+        D(ERR, "unable to create temporary bio");
+        close(*out_fd);
+        return -1;
+    }
+
+    ret = CMS_verify(content_info, NULL ,store, content, output_temp, 0);
+
+    if (ret == 0) {
+        char buf[256];
+        unsigned long err = ERR_peek_last_error();
+        D(ERR, "Verification failed with reason: %s, %s", ERR_lib_error_string(err),  ERR_error_string(err, buf));
+        D(ERR, "Data used: content %d", (int) content);
+    }
+
+    ERR_clear_error();
+    ERR_remove_state(0);
+
+    BIO_free(output_temp);
+
+    return ret;
+}
+
+void cert_release(BIO *content, CMS_ContentInfo *info) {
+    BIO_free(content);
+    CMS_ContentInfo_free(info);
+}
+
diff --git a/fastbootd/secure.h b/fastbootd/secure.h
new file mode 100644
index 0000000..878a643
--- /dev/null
+++ b/fastbootd/secure.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _FASTBOOTD_SECURE_H
+#define _FASTBOOTD_SECURE_H
+
+#include <openssl/ssl.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+#include <openssl/pem.h>
+#include <openssl/cms.h>
+
+void cert_init_crypto();
+
+X509_STORE *cert_store_from_path(const char*stream);
+
+static inline void cert_release_store(X509_STORE *store) {
+    X509_STORE_free(store);
+}
+
+int cert_read(int fd, CMS_ContentInfo **content, BIO **output);
+int cert_verify(BIO *content, CMS_ContentInfo *content_info, X509_STORE *store, int *out_fd);
+void cert_release(BIO *content, CMS_ContentInfo *info);
+
+#endif
diff --git a/fastbootd/socket_client.c b/fastbootd/socket_client.c
new file mode 100644
index 0000000..da636db
--- /dev/null
+++ b/fastbootd/socket_client.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <cutils/sockets.h>
+#include <poll.h>
+#include <unistd.h>
+
+#include "utils.h"
+
+#define BUFFER_SIZE 256
+
+#define STDIN_FD 0
+#define STDOUT_FD 1
+#define STDERR_FD 2
+
+void run_socket_client() {
+    int fd;
+    char buffer[BUFFER_SIZE];
+    int n;
+    struct pollfd fds[2];
+
+    fd = socket_local_client("fastbootd",
+                         ANDROID_SOCKET_NAMESPACE_RESERVED,
+                         SOCK_STREAM);
+
+    if (fd < 0) {
+        fprintf(stderr, "ERROR: Unable to open fastbootd socket\n");
+        return;
+    }
+
+    fds[0].fd = STDIN_FD;
+    fds[0].events = POLLIN;
+    fds[1].fd = fd;
+    fds[1].events = POLLIN;
+
+    while(true) {
+        if (poll(fds, 2, -1) <= 0) {
+            fprintf(stderr, "ERROR: socket error");
+            return;
+        }
+
+        if (fds[0].revents & POLLIN) {
+            if ((n = read(STDIN_FD, buffer, BUFFER_SIZE)) < 0) {
+                goto error;
+            }
+
+            if (bulk_write(fd, buffer, n) < 0) {
+                goto error;
+            }
+        }
+
+        if (fds[1].revents & POLLIN) {
+            if ((n = read(fd, buffer, BUFFER_SIZE)) < 0) {
+                goto error;
+            }
+
+            if (bulk_write(STDOUT_FD, buffer, n) < 0) {
+                goto error;
+            }
+        }
+    }
+
+error:
+    fprintf(stderr, "Transport error\n");
+}
diff --git a/fastbootd/socket_client.h b/fastbootd/socket_client.h
new file mode 100644
index 0000000..4481ff2
--- /dev/null
+++ b/fastbootd/socket_client.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _FASTBOOTD_SOCKET_CLIENT_H
+#define _FASTBOOTD_SOCKET_CLIENT_H
+
+void run_socket_client();
+
+#endif
diff --git a/fastbootd/transport.c b/fastbootd/transport.c
index 01a5a8a..19a705c 100644
--- a/fastbootd/transport.c
+++ b/fastbootd/transport.c
@@ -99,6 +99,7 @@
         }
         if (ret > 0) {
             buffer[ret] = 0;
+            //TODO: multiple threads
             protocol_handle_command(phandle, buffer);
         }
     }
diff --git a/fastbootd/transport_socket.c b/fastbootd/transport_socket.c
new file mode 100644
index 0000000..ff0f3bd
--- /dev/null
+++ b/fastbootd/transport_socket.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <cutils/sockets.h>
+
+#include "debug.h"
+#include "transport.h"
+#include "utils.h"
+
+
+#define container_of(ptr, type, member) \
+    ((type*)((char*)(ptr) - offsetof(type, member)))
+
+#define SOCKET_WORKING 0
+#define SOCKET_STOPPED -1
+
+
+struct socket_transport {
+    struct transport transport;
+
+    int fd;
+};
+
+struct socket_handle {
+    struct transport_handle handle;
+
+    int fd;
+};
+
+void socket_close(struct transport_handle *thandle)
+{
+    struct socket_handle * handle = container_of(thandle, struct socket_handle, handle);
+    close(handle->fd);
+}
+
+struct transport_handle *socket_connect(struct transport *transport)
+{
+    struct socket_handle *handle = calloc(sizeof(struct socket_handle), 1);
+    struct socket_transport *socket_transport = container_of(transport, struct socket_transport, transport);
+    struct sockaddr addr;
+    socklen_t alen = sizeof(addr);
+
+    handle->fd = accept(socket_transport->fd, &addr, &alen);
+
+    if (handle->fd < 0) {
+        D(WARN, "socket connect error");
+        return NULL;
+    }
+
+    D(DEBUG, "[ socket_thread - registering device ]");
+    return &handle->handle;
+}
+
+ssize_t socket_write(struct transport_handle *thandle, const void *data, size_t len)
+{
+    ssize_t ret;
+    struct socket_handle *handle = container_of(thandle, struct socket_handle, handle);
+
+    D(DEBUG, "about to write (fd=%d, len=%d)", handle->fd, len);
+    ret = bulk_write(handle->fd, data, len);
+    if (ret < 0) {
+        D(ERR, "ERROR: fd = %d, ret = %zd", handle->fd, ret);
+        return -1;
+    }
+    D(DEBUG, "[ socket_write done fd=%d ]", handle->fd);
+    return ret;
+}
+
+ssize_t socket_read(struct transport_handle *thandle, void *data, size_t len)
+{
+    ssize_t ret;
+    struct socket_handle *handle = container_of(thandle, struct socket_handle, handle);
+
+    D(DEBUG, "about to read (fd=%d, len=%d)", handle->fd, len);
+    ret = bulk_read(handle->fd, data, len);
+    if (ret < 0) {
+        D(ERR, "ERROR: fd = %d, ret = %zd", handle->fd, ret);
+        return -1;
+    }
+    D(DEBUG, "[ socket_read done fd=%d ret=%zd]", handle->fd, ret);
+    return ret;
+}
+
+static int listen_socket_init(struct socket_transport *socket_transport)
+{
+    int s = android_get_control_socket("fastbootd");
+
+    if (s < 0) {
+        D(WARN, "android_get_control_socket(fastbootd): %s\n", strerror(errno));
+        return 0;
+    }
+
+    if (listen(s, 4) < 0) {
+        D(WARN, "listen(control socket): %s\n", strerror(errno));
+        return 0;
+    }
+
+    socket_transport->fd = s;
+
+    return 1;
+}
+
+
+int transport_socket_init()
+{
+    struct socket_transport *socket_transport = malloc(sizeof(struct socket_transport));
+
+    socket_transport->transport.connect = socket_connect;
+    socket_transport->transport.close = socket_close;
+    socket_transport->transport.read = socket_read;
+    socket_transport->transport.write = socket_write;
+
+    if (!listen_socket_init(socket_transport)) {
+        D(ERR, "socket transport init failed");
+        free(socket_transport);
+        return 0;
+    }
+
+    transport_register(&socket_transport->transport);
+    return 1;
+}
+
diff --git a/fastbootd/trigger.c b/fastbootd/trigger.c
new file mode 100644
index 0000000..e63e64d
--- /dev/null
+++ b/fastbootd/trigger.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <dlfcn.h>
+
+#include <hardware/hardware.h>
+#include "debug.h"
+#include "trigger.h"
+#include "protocol.h"
+#include "vendor_trigger.h"
+
+static const int version = 1;
+
+static struct vendor_trigger_t *triggers = NULL;
+
+int load_trigger() {
+    int err;
+    hw_module_t* module;
+    hw_device_t* device;
+    int libversion;
+
+    err = hw_get_module(TRIGGER_MODULE_ID, (hw_module_t const**)&module);
+
+    if (err == 0) {
+        err = module->methods->open(module, NULL, &device);
+
+        if (err == 0) {
+            triggers = (struct vendor_trigger_t *) device;
+        } else {
+            D(WARN, "Libvendor load error");
+            return 1;
+        }
+    }
+    else {
+        D(WARN, "Libvendor not load: %s", strerror(-err));
+        return 0;
+    }
+
+    if (triggers->check_version != NULL &&
+        triggers->check_version(version, &libversion)) {
+
+        triggers = NULL;
+        D(ERR, "Library report incompability");
+        return 1;
+    }
+    D(INFO, "libvendortrigger loaded");
+
+    return 0;
+}
+
+int trigger_oem_cmd(const char *arg, const char **response) {
+    if (triggers != NULL && triggers->oem_cmd != NULL)
+        return triggers->oem_cmd(arg, response);
+    return 0;
+}
+
+int trigger_gpt_layout(struct GPT_content *table) {
+    if (triggers != NULL && triggers->gpt_layout != NULL)
+        return triggers->gpt_layout(table);
+    return 0;
+}
+
diff --git a/fastbootd/trigger.h b/fastbootd/trigger.h
new file mode 100644
index 0000000..404acb4
--- /dev/null
+++ b/fastbootd/trigger.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef __FASTBOOTD_TRIGGER_H_
+#define __FASTBOOTD_TRIGGER_H_
+
+#include "commands/partitions.h"
+#include "vendor_trigger.h"
+
+int load_trigger();
+
+/* same as in struct triggers */
+
+int trigger_gpt_layout(struct GPT_content *table);
+int trigger_oem_cmd(const char *arg, const char **response);
+
+#endif
diff --git a/fastbootd/usb_linux_client.c b/fastbootd/usb_linux_client.c
index 111cf35..7a8e46f 100644
--- a/fastbootd/usb_linux_client.c
+++ b/fastbootd/usb_linux_client.c
@@ -30,6 +30,7 @@
 
 #include "debug.h"
 #include "transport.h"
+#include "utils.h"
 
 #define   TRACE_TAG  TRACE_USB
 
@@ -50,8 +51,6 @@
 #define USB_FFS_FASTBOOT_OUT   USB_FFS_FASTBOOT_EP(ep1)
 #define USB_FFS_FASTBOOT_IN    USB_FFS_FASTBOOT_EP(ep2)
 
-#define READ_BUF_SIZE (16*1024)
-
 #define container_of(ptr, type, member) \
     ((type*)((char*)(ptr) - offsetof(type, member)))
 
@@ -212,26 +211,6 @@
     return -1;
 }
 
-static ssize_t bulk_write(int bulk_in, const char *buf, size_t length)
-{
-    size_t count = 0;
-    ssize_t ret;
-
-    do {
-        ret = TEMP_FAILURE_RETRY(write(bulk_in, buf + count, length - count));
-        if (ret < 0) {
-            D(WARN, "[ bulk_read failed fd=%d length=%d errno=%d %s ]",
-                    bulk_in, length, errno, strerror(errno));
-            return -1;
-        } else {
-            count += ret;
-        }
-    } while (count < length);
-
-    D(VERBOSE, "[ bulk_write done fd=%d ]", bulk_in);
-    return count;
-}
-
 static ssize_t usb_write(struct transport_handle *thandle, const void *data, size_t len)
 {
     ssize_t ret;
@@ -248,30 +227,6 @@
     return ret;
 }
 
-static ssize_t bulk_read(int bulk_out, char *buf, size_t length)
-{
-    ssize_t ret;
-    size_t n = 0;
-
-    while (n < length) {
-        size_t to_read = (length - n > READ_BUF_SIZE) ? READ_BUF_SIZE : length - n;
-        ret = TEMP_FAILURE_RETRY(read(bulk_out, buf + n, to_read));
-        if (ret < 0) {
-            D(WARN, "[ bulk_read failed fd=%d length=%d errno=%d %s ]",
-                    bulk_out, length, errno, strerror(errno));
-            return ret;
-        }
-        n += ret;
-        if (ret < (ssize_t)to_read) {
-            D(VERBOSE, "bulk_read short read, ret=%zd to_read=%u n=%u length=%u",
-                    ret, to_read, n, length);
-            break;
-        }
-    }
-
-    return n;
-}
-
 ssize_t usb_read(struct transport_handle *thandle, void *data, size_t len)
 {
     ssize_t ret;
diff --git a/fastbootd/utils.c b/fastbootd/utils.c
new file mode 100644
index 0000000..fe3f0f8
--- /dev/null
+++ b/fastbootd/utils.c
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <linux/fs.h>
+#include <stdlib.h>
+#include <cutils/properties.h>
+
+#include "utils.h"
+#include "debug.h"
+
+#ifndef BLKDISCARD
+#define BLKDISCARD _IO(0x12,119)
+#endif
+
+#ifndef BLKSECDISCARD
+#define BLKSECDISCARD _IO(0x12,125)
+#endif
+
+#define READ_BUF_SIZE (16*1024)
+
+int get_stream_size(FILE *stream) {
+    int size;
+    fseek(stream, 0, SEEK_END);
+    size = ftell(stream);
+    fseek(stream, 0, SEEK_SET);
+    return size;
+}
+
+uint64_t get_block_device_size(int fd)
+{
+    uint64_t size = 0;
+    int ret;
+
+    ret = ioctl(fd, BLKGETSIZE64, &size);
+
+    if (ret)
+        return 0;
+
+    return size;
+}
+
+uint64_t get_file_size(int fd)
+{
+    struct stat buf;
+    int ret;
+    int64_t computed_size;
+
+    ret = fstat(fd, &buf);
+    if (ret)
+        return 0;
+
+    if (S_ISREG(buf.st_mode))
+        computed_size = buf.st_size;
+    else if (S_ISBLK(buf.st_mode))
+        computed_size = get_block_device_size(fd);
+    else
+        computed_size = 0;
+
+    return computed_size;
+}
+
+uint64_t get_file_size64(int fd)
+{
+    struct stat64 buf;
+    int ret;
+    uint64_t computed_size;
+
+    ret = fstat64(fd, &buf);
+    if (ret)
+        return 0;
+
+    if (S_ISREG(buf.st_mode))
+        computed_size = buf.st_size;
+    else if (S_ISBLK(buf.st_mode))
+        computed_size = get_block_device_size(fd);
+    else
+        computed_size = 0;
+
+    return computed_size;
+}
+
+
+char *strip(char *str)
+{
+    int n;
+
+    n = strspn(str, " \t");
+    str += n;
+    n = strcspn(str, " \t");
+    str[n] = '\0';
+
+    return str;
+}
+
+int wipe_block_device(int fd, int64_t len)
+{
+    uint64_t range[2];
+    int ret;
+
+    range[0] = 0;
+    range[1] = len;
+    ret = ioctl(fd, BLKSECDISCARD, &range);
+    if (ret < 0) {
+        range[0] = 0;
+        range[1] = len;
+        ret = ioctl(fd, BLKDISCARD, &range);
+        if (ret < 0) {
+            D(WARN, "Discard failed\n");
+            return 1;
+        } else {
+            D(WARN, "Wipe via secure discard failed, used discard instead\n");
+            return 0;
+        }
+    }
+
+    return 0;
+}
+
+int create_temp_file() {
+    char tempname[] = "/dev/fastboot_data_XXXXXX";
+    int fd;
+
+    fd = mkstemp(tempname);
+    if (fd < 0)
+        return -1;
+
+    unlink(tempname);
+
+    return fd;
+}
+
+ssize_t bulk_write(int bulk_in, const char *buf, size_t length)
+{
+    size_t count = 0;
+    ssize_t ret;
+
+    do {
+        ret = TEMP_FAILURE_RETRY(write(bulk_in, buf + count, length - count));
+        if (ret < 0) {
+            D(WARN, "[ bulk_write failed fd=%d length=%d errno=%d %s ]",
+                    bulk_in, length, errno, strerror(errno));
+            return -1;
+        } else {
+            count += ret;
+        }
+    } while (count < length);
+
+    D(VERBOSE, "[ bulk_write done fd=%d ]", bulk_in);
+    return count;
+}
+
+ssize_t bulk_read(int bulk_out, char *buf, size_t length)
+{
+    ssize_t ret;
+    size_t n = 0;
+
+    while (n < length) {
+        size_t to_read = (length - n > READ_BUF_SIZE) ? READ_BUF_SIZE : length - n;
+        ret = TEMP_FAILURE_RETRY(read(bulk_out, buf + n, to_read));
+        if (ret < 0) {
+            D(WARN, "[ bulk_read failed fd=%d length=%d errno=%d %s ]",
+                    bulk_out, length, errno, strerror(errno));
+            return ret;
+        }
+        n += ret;
+        if (ret < (ssize_t)to_read) {
+            D(VERBOSE, "bulk_read short read, ret=%zd to_read=%u n=%u length=%u",
+                    ret, to_read, n, length);
+            break;
+        }
+    }
+
+    return n;
+}
+
+#define NAP_TIME 200  // 200 ms between polls
+static int wait_for_property(const char *name, const char *desired_value, int maxwait)
+{
+    char value[PROPERTY_VALUE_MAX] = {'\0'};
+    int maxnaps = (maxwait * 1000) / NAP_TIME;
+
+    if (maxnaps < 1) {
+        maxnaps = 1;
+    }
+
+    while (maxnaps-- > 0) {
+        usleep(NAP_TIME * 1000);
+        if (property_get(name, value, NULL)) {
+            if (desired_value == NULL || strcmp(value, desired_value) == 0) {
+                return 0;
+            }
+        }
+    }
+    return -1; /* failure */
+}
+
+int service_start(const char *service_name)
+{
+    int result = 0;
+    char property_value[PROPERTY_VALUE_MAX];
+
+    property_get(service_name, property_value, "");
+    if (strcmp("running", property_value) != 0) {
+        D(INFO, "Starting %s", service_name);
+        property_set("ctl.start", service_name);
+        if (wait_for_property(service_name, "running", 5))
+            result = -1;
+    }
+
+    return result;
+}
+
+int service_stop(const char *service_name)
+{
+    int result = 0;
+
+    D(INFO, "Stopping MDNSD");
+    property_set("ctl.stop", service_name);
+    if (wait_for_property(service_name, "stopped", 5))
+        result = -1;
+
+    return result;
+}
+
+int ssh_server_start()
+{
+    return service_start("sshd");
+}
diff --git a/fastbootd/utils.h b/fastbootd/utils.h
new file mode 100644
index 0000000..3d98699
--- /dev/null
+++ b/fastbootd/utils.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _FASTBOOT_UTLIS_H
+#define _FASTBOOT_UTILS_H
+
+#include <stdio.h>
+
+int get_stream_size(FILE *);
+
+char *strip(char *str);
+
+uint64_t get_file_size64(int fd);
+uint64_t get_file_size(int fd);
+uint64_t get_block_device_size(int fd);
+int wipe_block_device(int fd, int64_t len);
+int create_temp_file();
+ssize_t bulk_read(int bulk_out, char *buf, size_t length);
+ssize_t bulk_write(int bulk_in, const char *buf, size_t length);
+int service_start(const char *service_name);
+int service_stop(const char *service_name);
+int ssh_server_start();
+
+#define ROUND_TO_PAGE(address,pagesize) ((address + pagesize - 1) & (~(pagesize - 1)))
+
+#define ROUND_UP(number,size) (((number + size - 1) / size) * size)
+
+#endif
diff --git a/include/ion/ion.h b/include/ion/ion.h
index 018c0a1..fbd34e2 100644
--- a/include/ion/ion.h
+++ b/include/ion/ion.h
@@ -25,6 +25,8 @@
 
 __BEGIN_DECLS
 
+struct ion_handle;
+
 int ion_open();
 int ion_close(int fd);
 int ion_alloc(int fd, size_t len, size_t align, unsigned int heap_mask,
diff --git a/include/sysutils/NetlinkEvent.h b/include/sysutils/NetlinkEvent.h
index f3501cf..c0a9418 100644
--- a/include/sysutils/NetlinkEvent.h
+++ b/include/sysutils/NetlinkEvent.h
@@ -36,6 +36,7 @@
     const static int NlActionLinkUp;
     const static int NlActionAddressUpdated;
     const static int NlActionAddressRemoved;
+    const static int NlActionRdnss;
 
     NetlinkEvent();
     virtual ~NetlinkEvent();
@@ -49,9 +50,10 @@
     void dump();
 
  protected:
-    bool parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr, int rtasize);
     bool parseBinaryNetlinkMessage(char *buffer, int size);
     bool parseAsciiNetlinkMessage(char *buffer, int size);
+    bool parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr, int rtasize);
+    bool parseNdUserOptMessage(struct nduseroptmsg *msg, int optsize);
 };
 
 #endif
diff --git a/init/devices.c b/init/devices.c
index af88c5f..f7df453 100644
--- a/init/devices.c
+++ b/init/devices.c
@@ -44,6 +44,7 @@
 #include <cutils/uevent.h>
 
 #include "devices.h"
+#include "ueventd_parser.h"
 #include "util.h"
 #include "log.h"
 
@@ -530,8 +531,11 @@
     name++;
 
     /* too-long names would overrun our buffer */
-    if(strlen(name) > len)
+    if(strlen(name) > len) {
+        ERROR("DEVPATH=%s exceeds %u-character limit on filename; ignoring event\n",
+                name, len);
         return NULL;
+    }
 
     return name;
 }
@@ -557,37 +561,76 @@
             uevent->major, uevent->minor, links);
 }
 
+#define DEVPATH_LEN 96
+
+static bool assemble_devpath(char *devpath, const char *dirname,
+        const char *devname)
+{
+    int s = snprintf(devpath, DEVPATH_LEN, "%s/%s", dirname, devname);
+    if (s < 0) {
+        ERROR("failed to assemble device path (%s); ignoring event\n",
+                strerror(errno));
+        return false;
+    } else if (s >= DEVPATH_LEN) {
+        ERROR("%s/%s exceeds %u-character limit on path; ignoring event\n",
+                dirname, devname, DEVPATH_LEN);
+        return false;
+    }
+    return true;
+}
+
+static void mkdir_recursive_for_devpath(const char *devpath)
+{
+    char dir[DEVPATH_LEN];
+    char *slash;
+
+    strcpy(dir, devpath);
+    slash = strrchr(dir, '/');
+    *slash = '\0';
+    mkdir_recursive(dir, 0755);
+}
+
 static void handle_generic_device_event(struct uevent *uevent)
 {
     char *base;
     const char *name;
-    char devpath[96] = {0};
+    char devpath[DEVPATH_LEN] = {0};
     char **links = NULL;
 
     name = parse_device_name(uevent, 64);
     if (!name)
         return;
 
-    if (!strncmp(uevent->subsystem, "usb", 3)) {
+    struct ueventd_subsystem *subsystem =
+            ueventd_subsystem_find_by_name(uevent->subsystem);
+
+    if (subsystem) {
+        const char *devname;
+
+        switch (subsystem->devname_src) {
+        case DEVNAME_UEVENT_DEVNAME:
+            devname = uevent->device_name;
+            break;
+
+        case DEVNAME_UEVENT_DEVPATH:
+            devname = name;
+            break;
+
+        default:
+            ERROR("%s subsystem's devpath option is not set; ignoring event\n",
+                    uevent->subsystem);
+            return;
+        }
+
+        if (!assemble_devpath(devpath, subsystem->dirname, devname))
+            return;
+        mkdir_recursive_for_devpath(devpath);
+    } else if (!strncmp(uevent->subsystem, "usb", 3)) {
          if (!strcmp(uevent->subsystem, "usb")) {
             if (uevent->device_name) {
-                /*
-                 * create device node provided by kernel if present
-                 * see drivers/base/core.c
-                 */
-                char *p = devpath;
-                snprintf(devpath, sizeof(devpath), "/dev/%s", uevent->device_name);
-                /* skip leading /dev/ */
-                p += 5;
-                /* build directories */
-                while (*p) {
-                    if (*p == '/') {
-                        *p = 0;
-                        make_dir(devpath, 0755);
-                        *p = '/';
-                    }
-                    p++;
-                }
+                if (!assemble_devpath(devpath, "/dev", uevent->device_name))
+                    return;
+                mkdir_recursive_for_devpath(devpath);
              }
              else {
                  /* This imitates the file system that would be created
diff --git a/init/init_parser.c b/init/init_parser.c
index 3f0838f..f49e698 100644
--- a/init/init_parser.c
+++ b/init/init_parser.c
@@ -60,7 +60,7 @@
 #define KEYWORD(symbol, flags, nargs, func) \
     [ K_##symbol ] = { #symbol, func, nargs + 1, flags, },
 
-struct {
+static struct {
     const char *name;
     int (*func)(int nargs, char **args);
     unsigned char nargs;
@@ -76,7 +76,7 @@
 #define kw_func(kw) (keyword_info[kw].func)
 #define kw_nargs(kw) (keyword_info[kw].nargs)
 
-int lookup_keyword(const char *s)
+static int lookup_keyword(const char *s)
 {
     switch (*s++) {
     case 'c':
@@ -170,7 +170,7 @@
     return K_UNKNOWN;
 }
 
-void parse_line_no_op(struct parse_state *state, int nargs, char **args)
+static void parse_line_no_op(struct parse_state *state, int nargs, char **args)
 {
 }
 
@@ -293,7 +293,7 @@
     return -1;
 }
 
-void parse_import(struct parse_state *state, int nargs, char **args)
+static void parse_import(struct parse_state *state, int nargs, char **args)
 {
     struct listnode *import_list = state->priv;
     struct import *import;
@@ -318,7 +318,7 @@
     INFO("found import '%s', adding to import list", import->filename);
 }
 
-void parse_new_section(struct parse_state *state, int kw,
+static void parse_new_section(struct parse_state *state, int kw,
                        int nargs, char **args)
 {
     printf("[ %s %s ]\n", args[0],
diff --git a/init/parser.h b/init/parser.h
index 0a5802a..a58272a 100644
--- a/init/parser.h
+++ b/init/parser.h
@@ -33,7 +33,6 @@
     void *priv;
 };
 
-int lookup_keyword(const char *s);
 void DUMP(void);
 int next_token(struct parse_state *state);
 void parse_error(struct parse_state *state, const char *fmt, ...);
diff --git a/init/signal_handler.c b/init/signal_handler.c
index d31ad63..7e8e1a7 100644
--- a/init/signal_handler.c
+++ b/init/signal_handler.c
@@ -57,7 +57,15 @@
 
     svc = service_find_by_pid(pid);
     if (!svc) {
-        ERROR("untracked pid %d exited\n", pid);
+        if (WIFEXITED(status)) {
+            ERROR("untracked pid %d exited with status %d\n", pid, WEXITSTATUS(status));
+        } else if (WIFSIGNALED(status)) {
+            ERROR("untracked pid %d killed by signal %d\n", pid, WTERMSIG(status));
+        } else if (WIFSTOPPED(status)) {
+            ERROR("untracked pid %d stopped by signal %d\n", pid, WSTOPSIG(status));
+        } else {
+            ERROR("untracked pid %d state changed\n", pid);
+        }
         return 0;
     }
 
diff --git a/init/ueventd.h b/init/ueventd.h
index 9066e47..0a454c5 100644
--- a/init/ueventd.h
+++ b/init/ueventd.h
@@ -17,6 +17,21 @@
 #ifndef _INIT_UEVENTD_H_
 #define _INIT_UEVENTD_H_
 
+#include <cutils/list.h>
+#include <sys/types.h>
+
+struct ueventd_subsystem {
+    struct listnode slist;
+
+    const char *name;
+    enum {
+        DEVNAME_UNKNOWN = 0,
+        DEVNAME_UEVENT_DEVNAME,
+        DEVNAME_UEVENT_DEVPATH,
+    } devname_src;
+    const char *dirname;
+};
+
 int ueventd_main(int argc, char **argv);
 
 #endif
diff --git a/init/ueventd_keywords.h b/init/ueventd_keywords.h
new file mode 100644
index 0000000..88e8f01
--- /dev/null
+++ b/init/ueventd_keywords.h
@@ -0,0 +1,15 @@
+#ifndef KEYWORD
+#define __MAKE_KEYWORD_ENUM__
+#define KEYWORD(symbol, flags, nargs) K_##symbol,
+enum {
+    K_UNKNOWN,
+#endif
+    KEYWORD(subsystem,      SECTION,    1)
+    KEYWORD(devname,        OPTION,     1)
+    KEYWORD(dirname,        OPTION,     1)
+#ifdef __MAKE_KEYWORD_ENUM__
+    KEYWORD_COUNT,
+};
+#undef __MAKE_KEYWORD_ENUM__
+#undef KEYWORD
+#endif
diff --git a/init/ueventd_parser.c b/init/ueventd_parser.c
index 3e60df5..e447006 100644
--- a/init/ueventd_parser.c
+++ b/init/ueventd_parser.c
@@ -14,18 +14,189 @@
  * limitations under the License.
  */
 
+#include <ctype.h>
+#include <errno.h>
 #include <stdio.h>
 #include <unistd.h>
 #include <stdarg.h>
+#include <stdlib.h>
 #include <string.h>
 
+#include "ueventd.h"
 #include "ueventd_parser.h"
 #include "parser.h"
 #include "log.h"
 #include "util.h"
 
+static list_declare(subsystem_list);
+
 static void parse_line_device(struct parse_state *state, int nargs, char **args);
 
+#define SECTION 0x01
+#define OPTION  0x02
+
+#include "ueventd_keywords.h"
+
+#define KEYWORD(symbol, flags, nargs) \
+    [ K_##symbol ] = { #symbol, nargs + 1, flags, },
+
+static struct {
+    const char *name;
+    unsigned char nargs;
+    unsigned char flags;
+} keyword_info[KEYWORD_COUNT] = {
+    [ K_UNKNOWN ] = { "unknown", 0, 0 },
+#include "ueventd_keywords.h"
+};
+#undef KEYWORD
+
+#define kw_is(kw, type) (keyword_info[kw].flags & (type))
+#define kw_nargs(kw) (keyword_info[kw].nargs)
+
+static int lookup_keyword(const char *s)
+{
+    switch (*s++) {
+    case 'd':
+        if (!strcmp(s, "evname")) return K_devname;
+        if (!strcmp(s, "irname")) return K_dirname;
+        break;
+    case 's':
+        if (!strcmp(s, "ubsystem")) return K_subsystem;
+        break;
+    }
+    return K_UNKNOWN;
+}
+
+static void parse_line_no_op(struct parse_state *state __attribute__((unused)),
+        int nargs __attribute__((unused)), char **args  __attribute__((unused)))
+{
+}
+
+static int valid_name(const char *name)
+{
+    while (*name) {
+        if (!isalnum(*name) && (*name != '_') && (*name != '-')) {
+            return 0;
+        }
+        name++;
+    }
+    return 1;
+}
+
+struct ueventd_subsystem *ueventd_subsystem_find_by_name(const char *name)
+{
+    struct listnode *node;
+    struct ueventd_subsystem *s;
+
+    list_for_each(node, &subsystem_list) {
+        s = node_to_item(node, struct ueventd_subsystem, slist);
+        if (!strcmp(s->name, name)) {
+            return s;
+        }
+    }
+    return 0;
+}
+
+static void *parse_subsystem(struct parse_state *state,
+        int nargs __attribute__((unused)), char **args)
+{
+    struct ueventd_subsystem *s;
+
+    if (!valid_name(args[1])) {
+        parse_error(state, "invalid subsystem name '%s'\n", args[1]);
+        return 0;
+    }
+
+    s = ueventd_subsystem_find_by_name(args[1]);
+    if (s) {
+        parse_error(state, "ignored duplicate definition of subsystem '%s'\n",
+                args[1]);
+        return 0;
+    }
+
+    s = calloc(1, sizeof(*s));
+    if (!s) {
+        parse_error(state, "out of memory\n");
+        return 0;
+    }
+    s->name = args[1];
+    s->dirname = "/dev";
+    list_add_tail(&subsystem_list, &s->slist);
+    return s;
+}
+
+static void parse_line_subsystem(struct parse_state *state, int nargs,
+        char **args)
+{
+    struct ueventd_subsystem *s = state->context;
+    int kw;
+
+    if (nargs == 0) {
+        return;
+    }
+
+    kw = lookup_keyword(args[0]);
+    switch (kw) {
+    case K_devname:
+        if (!strcmp(args[1], "uevent_devname"))
+            s->devname_src = DEVNAME_UEVENT_DEVNAME;
+        else if (!strcmp(args[1], "uevent_devpath"))
+            s->devname_src = DEVNAME_UEVENT_DEVPATH;
+        else
+            parse_error(state, "invalid devname '%s'\n", args[1]);
+        break;
+
+    case K_dirname:
+        if (args[1][0] == '/')
+            s->dirname = args[1];
+        else
+            parse_error(state, "dirname '%s' does not start with '/'\n",
+                    args[1]);
+        break;
+
+    default:
+        parse_error(state, "invalid option '%s'\n", args[0]);
+    }
+}
+
+static void parse_new_section(struct parse_state *state, int kw,
+                       int nargs, char **args)
+{
+    printf("[ %s %s ]\n", args[0],
+           nargs > 1 ? args[1] : "");
+
+    switch(kw) {
+    case K_subsystem:
+        state->context = parse_subsystem(state, nargs, args);
+        if (state->context) {
+            state->parse_line = parse_line_subsystem;
+            return;
+        }
+        break;
+    }
+    state->parse_line = parse_line_no_op;
+}
+
+static void parse_line(struct parse_state *state, char **args, int nargs)
+{
+    int kw = lookup_keyword(args[0]);
+    int kw_nargs = kw_nargs(kw);
+
+    if (nargs < kw_nargs) {
+        parse_error(state, "%s requires %d %s\n", args[0], kw_nargs - 1,
+            kw_nargs > 2 ? "arguments" : "argument");
+        return;
+    }
+
+    if (kw_is(kw, SECTION)) {
+        parse_new_section(state, kw, nargs, args);
+    } else if (kw_is(kw, OPTION)) {
+        state->parse_line(state, nargs, args);
+    } else {
+        parse_line_device(state, nargs, args);
+    }
+}
+
 static void parse_config(const char *fn, char *s)
 {
     struct parse_state state;
@@ -36,18 +207,19 @@
     state.line = 1;
     state.ptr = s;
     state.nexttoken = 0;
-    state.parse_line = parse_line_device;
+    state.parse_line = parse_line_no_op;
     for (;;) {
         int token = next_token(&state);
         switch (token) {
         case T_EOF:
-            state.parse_line(&state, 0, 0);
+            parse_line(&state, args, nargs);
             return;
         case T_NEWLINE:
             if (nargs) {
-                state.parse_line(&state, nargs, args);
+                parse_line(&state, args, nargs);
                 nargs = 0;
             }
+            state.line++;
             break;
         case T_TEXT:
             if (nargs < UEVENTD_PARSER_MAXARGS) {
@@ -69,7 +241,8 @@
     return 0;
 }
 
-static void parse_line_device(struct parse_state* state, int nargs, char **args)
+static void parse_line_device(struct parse_state *state __attribute__((unused)),
+        int nargs, char **args)
 {
     set_device_permission(nargs, args);
 }
diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h
index 3684285..907cc49 100644
--- a/init/ueventd_parser.h
+++ b/init/ueventd_parser.h
@@ -17,9 +17,12 @@
 #ifndef _INIT_UEVENTD_PARSER_H_
 #define _INIT_UEVENTD_PARSER_H_
 
+#include "ueventd.h"
+
 #define UEVENTD_PARSER_MAXARGS 5
 
 int ueventd_parse_config_file(const char *fn);
 void set_device_permission(int nargs, char **args);
+struct ueventd_subsystem *ueventd_subsystem_find_by_name(const char *name);
 
 #endif
diff --git a/libcutils/android_reboot.c b/libcutils/android_reboot.c
index 16f82bb..aef3054 100644
--- a/libcutils/android_reboot.c
+++ b/libcutils/android_reboot.c
@@ -16,6 +16,7 @@
 
 #include <unistd.h>
 #include <sys/reboot.h>
+#include <sys/syscall.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
@@ -118,7 +119,7 @@
             break;
 
         case ANDROID_RB_RESTART2:
-            ret = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
+            ret = syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
                            LINUX_REBOOT_CMD_RESTART2, arg);
             break;
 
diff --git a/libion/ion_test.c b/libion/ion_test.c
index 0caaa2a..d2c37ca 100644
--- a/libion/ion_test.c
+++ b/libion/ion_test.c
@@ -13,7 +13,6 @@
 
 #include <ion/ion.h>
 #include <linux/ion.h>
-#include <linux/omap_ion.h>
 
 size_t len = 1024*1024, align = 0;
 int prot = PROT_READ | PROT_WRITE;
diff --git a/libpixelflinger/scanline.cpp b/libpixelflinger/scanline.cpp
index a5d28b2..9663a2b 100644
--- a/libpixelflinger/scanline.cpp
+++ b/libpixelflinger/scanline.cpp
@@ -1877,7 +1877,7 @@
             struct {
                 int32_t s, sq;
                 int32_t t, tq;
-            };
+            } sqtq;
             struct {
                 int32_t v, q;
             } st[2];
@@ -1916,10 +1916,10 @@
         int32_t t =   tmu.shade.it0 +
                      (tmu.shade.idtdy * ys) + (tmu.shade.idtdx * xs) +
                      ((tmu.shade.idtdx + tmu.shade.idtdy)>>1);
-        tc[i].s  = s;
-        tc[i].t  = t;
-        tc[i].sq = gglMulx(s, q0, iwscale);
-        tc[i].tq = gglMulx(t, q0, iwscale);
+        tc[i].sqtq.s  = s;
+        tc[i].sqtq.t  = t;
+        tc[i].sqtq.sq = gglMulx(s, q0, iwscale);
+        tc[i].sqtq.tq = gglMulx(t, q0, iwscale);
     }
 
     int32_t span = 0;
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
index aae2ae7..34f2016 100644
--- a/libsysutils/src/NetlinkEvent.cpp
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -24,6 +24,7 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
+#include <netinet/icmp6.h>
 #include <arpa/inet.h>
 #include <net/if.h>
 
@@ -44,6 +45,7 @@
 const int NetlinkEvent::NlActionLinkDown = 5;
 const int NetlinkEvent::NlActionAddressUpdated = 6;
 const int NetlinkEvent::NlActionAddressRemoved = 7;
+const int NetlinkEvent::NlActionRdnss = 8;
 
 NetlinkEvent::NetlinkEvent() {
     mAction = NlActionUnknown;
@@ -76,7 +78,7 @@
 }
 
 /*
- * Decode a RTM_NEWADDR or RTM_DELADDR message.
+ * Parse a RTM_NEWADDR or RTM_DELADDR message.
  */
 bool NetlinkEvent::parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr,
                                       int rtasize) {
@@ -172,13 +174,112 @@
 }
 
 /*
- * Parse an binary message from a NETLINK_ROUTE netlink socket.
+<<<<<<< HEAD
+ * Parse a RTM_NEWNDUSEROPT message.
+ */
+bool NetlinkEvent::parseNdUserOptMessage(struct nduseroptmsg *msg, int len) {
+    // Check the length is valid.
+    if (msg->nduseropt_opts_len > len) {
+        SLOGE("RTM_NEWNDUSEROPT invalid length %d > %d\n",
+              msg->nduseropt_opts_len, len);
+        return false;
+    }
+    len = msg->nduseropt_opts_len;
+
+    // Check address family and packet type.
+    if (msg->nduseropt_family != AF_INET6) {
+        SLOGE("RTM_NEWNDUSEROPT message for unknown family %d\n",
+              msg->nduseropt_family);
+        return false;
+    }
+
+    if (msg->nduseropt_icmp_type != ND_ROUTER_ADVERT ||
+        msg->nduseropt_icmp_code != 0) {
+        SLOGE("RTM_NEWNDUSEROPT message for unknown ICMPv6 type/code %d/%d\n",
+              msg->nduseropt_icmp_type, msg->nduseropt_icmp_code);
+        return false;
+    }
+
+    // Find the interface name.
+    char ifname[IFNAMSIZ + 1];
+    if (!if_indextoname(msg->nduseropt_ifindex, ifname)) {
+        SLOGE("RTM_NEWNDUSEROPT on unknown ifindex %d\n",
+              msg->nduseropt_ifindex);
+        return false;
+    }
+
+    // The kernel sends a separate netlink message for each ND option in the RA.
+    // So only parse the first ND option in the message.
+    struct nd_opt_hdr *opthdr = (struct nd_opt_hdr *) (msg + 1);
+
+    // The length is in multiples of 8 octets.
+    uint16_t optlen = opthdr->nd_opt_len;
+    if (optlen * 8 > len) {
+        SLOGE("Invalid option length %d > %d for ND option %d\n",
+              optlen * 8, len, opthdr->nd_opt_type);
+        return false;
+    }
+
+    if (opthdr->nd_opt_type == ND_OPT_RDNSS) {
+        // DNS Servers (RFC 6106).
+        // Each address takes up 2*8 octets, and the header takes up 8 octets.
+        // So for a valid option with one or more addresses, optlen must be
+        // odd and greater than 1.
+        if ((optlen < 3) || !(optlen & 0x1)) {
+            SLOGE("Invalid optlen %d for RDNSS option\n", optlen);
+            return false;
+        }
+        int numaddrs = (optlen - 1) / 2;
+
+        // Find the lifetime.
+        struct nd_opt_rdnss *rndss_opt = (struct nd_opt_rdnss *) opthdr;
+        uint32_t lifetime = ntohl(rndss_opt->nd_opt_rdnss_lifetime);
+
+        // Construct "SERVERS=<comma-separated string of DNS addresses>".
+        // Reserve (INET6_ADDRSTRLEN + 1) chars for each address: all but the
+        // the last address are followed by ','; the last is followed by '\0'.
+        static const char kServerTag[] = "SERVERS=";
+        static const int kTagLength = sizeof(kServerTag) - 1;
+        int bufsize = kTagLength + numaddrs * (INET6_ADDRSTRLEN + 1);
+        char *buf = (char *) malloc(bufsize);
+        if (!buf) {
+            SLOGE("RDNSS option: out of memory\n");
+            return false;
+        }
+        strcpy(buf, kServerTag);
+        int pos = kTagLength;
+
+        struct in6_addr *addrs = (struct in6_addr *) (rndss_opt + 1);
+        for (int i = 0; i < numaddrs; i++) {
+            if (i > 0) {
+                buf[pos++] = ',';
+            }
+            inet_ntop(AF_INET6, addrs + i, buf + pos, bufsize - pos);
+            pos += strlen(buf + pos);
+        }
+        buf[pos] = '\0';
+
+        mAction = NlActionRdnss;
+        mSubsystem = strdup("net");
+        asprintf(&mParams[0], "INTERFACE=%s", ifname);
+        asprintf(&mParams[1], "LIFETIME=%u", lifetime);
+        mParams[2] = buf;
+    } else {
+        SLOGD("Unknown ND option type %d\n", opthdr->nd_opt_type);
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Parse a binary message from a NETLINK_ROUTE netlink socket.
  */
 bool NetlinkEvent::parseBinaryNetlinkMessage(char *buffer, int size) {
     const struct nlmsghdr *nh;
 
     for (nh = (struct nlmsghdr *) buffer;
-         NLMSG_OK(nh, size) && (nh->nlmsg_type != NLMSG_DONE);
+         NLMSG_OK(nh, (unsigned) size) && (nh->nlmsg_type != NLMSG_DONE);
          nh = NLMSG_NEXT(nh, size)) {
 
         if (nh->nlmsg_type == RTM_NEWLINK) {
@@ -245,8 +346,25 @@
             if (!parseIfAddrMessage(nh->nlmsg_type, ifa, rtasize)) {
                 continue;
             }
+
+        } else if (nh->nlmsg_type == RTM_NEWNDUSEROPT) {
+            int len = nh->nlmsg_len - sizeof(*nh);
+            struct nduseroptmsg *ndmsg = (struct nduseroptmsg *) NLMSG_DATA(nh);
+
+            if (sizeof(*ndmsg) > (size_t) len) {
+                SLOGE("Got a short RTM_NEWNDUSEROPT message\n");
+                continue;
+            }
+
+            size_t optsize = NLMSG_PAYLOAD(nh, sizeof(*ndmsg));
+            if (!parseNdUserOptMessage(ndmsg, optsize)) {
+                continue;
+            }
+
+
         } else {
-                SLOGD("Unexpected netlink message. type=0x%x\n", nh->nlmsg_type);
+                SLOGD("Unexpected netlink message. type=0x%x\n",
+                      nh->nlmsg_type);
         }
     }
 
diff --git a/logwrapper/logwrapper.c b/logwrapper/logwrapper.c
index d0d8d14..db6cb4c 100644
--- a/logwrapper/logwrapper.c
+++ b/logwrapper/logwrapper.c
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/wait.h>
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 400c09e..109f15b 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -361,17 +361,18 @@
 
 # Define TCP buffer sizes for various networks
 #   ReadMin, ReadInitial, ReadMax, WriteMin, WriteInitial, WriteMax,
-    setprop net.tcp.buffersize.default 4096,87380,110208,4096,16384,110208
-    setprop net.tcp.buffersize.wifi    524288,1048576,2097152,262144,524288,1048576
-    setprop net.tcp.buffersize.lte     524288,1048576,2097152,262144,524288,1048576
-    setprop net.tcp.buffersize.umts    4094,87380,110208,4096,16384,110208
-    setprop net.tcp.buffersize.hspa    4094,87380,262144,4096,16384,262144
-    setprop net.tcp.buffersize.hsupa   4094,87380,262144,4096,16384,262144
-    setprop net.tcp.buffersize.hsdpa   4094,87380,262144,4096,16384,262144
-    setprop net.tcp.buffersize.hspap   4094,87380,1220608,4096,16384,1220608
-    setprop net.tcp.buffersize.edge    4093,26280,35040,4096,16384,35040
-    setprop net.tcp.buffersize.gprs    4092,8760,11680,4096,8760,11680
-    setprop net.tcp.buffersize.evdo    4094,87380,262144,4096,16384,262144
+    setprop net.tcp.buffersize.default  4096,87380,110208,4096,16384,110208
+    setprop net.tcp.buffersize.wifi     524288,1048576,2097152,262144,524288,1048576
+    setprop net.tcp.buffersize.ethernet 524288,1048576,3145728,524288,1048576,2097152
+    setprop net.tcp.buffersize.lte      524288,1048576,2097152,262144,524288,1048576
+    setprop net.tcp.buffersize.umts     4094,87380,110208,4096,16384,110208
+    setprop net.tcp.buffersize.hspa     4094,87380,262144,4096,16384,262144
+    setprop net.tcp.buffersize.hsupa    4094,87380,262144,4096,16384,262144
+    setprop net.tcp.buffersize.hsdpa    4094,87380,262144,4096,16384,262144
+    setprop net.tcp.buffersize.hspap    4094,87380,1220608,4096,16384,1220608
+    setprop net.tcp.buffersize.edge     4093,26280,35040,4096,16384,35040
+    setprop net.tcp.buffersize.gprs     4092,8760,11680,4096,8760,11680
+    setprop net.tcp.buffersize.evdo     4094,87380,262144,4096,16384,262144
 
     class_start core
     class_start main
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index a60cfc5..ddf8753 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -1,3 +1,6 @@
+subsystem adf
+	devname uevent_devname
+
 /dev/null                 0666   root       root
 /dev/zero                 0666   root       root
 /dev/full                 0666   root       root
diff --git a/toolbox/ls.c b/toolbox/ls.c
index 5324511..c736958 100644
--- a/toolbox/ls.c
+++ b/toolbox/ls.c
@@ -75,23 +75,23 @@
     *out = 0;
 }
 
-static void user2str(unsigned uid, char *out)
+static void user2str(uid_t uid, char *out, size_t out_size)
 {
     struct passwd *pw = getpwuid(uid);
     if(pw) {
-        strcpy(out, pw->pw_name);
+        strlcpy(out, pw->pw_name, out_size);
     } else {
-        sprintf(out, "%d", uid);
+        snprintf(out, out_size, "%d", uid);
     }
 }
 
-static void group2str(unsigned gid, char *out)
+static void group2str(gid_t gid, char *out, size_t out_size)
 {
     struct group *gr = getgrgid(gid);
     if(gr) {
-        strcpy(out, gr->gr_name);
+        strlcpy(out, gr->gr_name, out_size);
     } else {
-        sprintf(out, "%d", gid);
+        snprintf(out, out_size, "%d", gid);
     }
 }
 
@@ -164,8 +164,8 @@
 {
     char date[32];
     char mode[16];
-    char user[16];
-    char group[16];
+    char user[32];
+    char group[32];
     const char *name;
 
     if(!s || !path) {
@@ -182,11 +182,11 @@
 
     mode2str(s->st_mode, mode);
     if (flags & LIST_LONG_NUMERIC) {
-        sprintf(user, "%ld", s->st_uid);
-        sprintf(group, "%ld", s->st_gid);
+        snprintf(user, sizeof(user), "%ld", s->st_uid);
+        snprintf(group, sizeof(group), "%ld", s->st_gid);
     } else {
-        user2str(s->st_uid, user);
-        group2str(s->st_gid, group);
+        user2str(s->st_uid, user, sizeof(user));
+        group2str(s->st_gid, group, sizeof(group));
     }
 
     strftime(date, 32, "%Y-%m-%d %H:%M", localtime((const time_t*)&s->st_mtime));
@@ -238,8 +238,8 @@
 static int listfile_maclabel(const char *path, struct stat *s, int flags)
 {
     char mode[16];
-    char user[16];
-    char group[16];
+    char user[32];
+    char group[32];
     char *maclabel = NULL;
     const char *name;
 
@@ -261,8 +261,8 @@
     }
 
     mode2str(s->st_mode, mode);
-    user2str(s->st_uid, user);
-    group2str(s->st_gid, group);
+    user2str(s->st_uid, user, sizeof(user));
+    group2str(s->st_gid, group, sizeof(group));
 
     switch(s->st_mode & S_IFMT) {
     case S_IFLNK: {