Add initial implementation of network backend 78/188078/3
authorDongwoo Lee <dwoo08.lee@samsung.com>
Thu, 2 Nov 2017 21:33:17 +0000 (06:33 +0900)
committerDongwoo Lee <dwoo08.lee@samsung.com>
Wed, 12 Sep 2018 02:06:04 +0000 (11:06 +0900)
This is initial version of flash-manager with network backend. The
entire structure would be evolved while the new feature is added.

Change-Id: I659c2795d339060afada9a9ffd5f41294c17f228
Signed-off-by: Dongwoo Lee <dwoo08.lee@samsung.com>
CMakeLists.txt [new file with mode: 0644]
packaging/initrd-flash.spec
src/dfu.c [new file with mode: 0644]
src/dfu.h [new file with mode: 0644]
src/main.c [new file with mode: 0644]
src/net.c [new file with mode: 0644]
src/net.h [new file with mode: 0644]
src/tfm.h [new file with mode: 0644]
src/thor-proto.h [new file with mode: 0644]
src/thor.c [new file with mode: 0644]

diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..8688aa1
--- /dev/null
@@ -0,0 +1,16 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
+PROJECT(flash-manager C)
+
+FIND_PACKAGE(Threads REQUIRED)
+
+ADD_EXECUTABLE(${PROJECT_NAME} src/main.c src/thor.c src/dfu.c src/net.c)
+
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/src)
+
+IF(CMAKE_THREAD_LIBS_INIT)
+       TARGET_LINK_LIBRARIES(flash-manager "${CMAKE_THREAD_LIBS_INIT}")
+ENDIF()
+
+ADD_DEFINITIONS(-Wall -g -O2)
+
+INSTALL(TARGETS ${PROJECT_NAME} DESTINATION bin)
index 2a3486b9e1acb7150273a0673c329c801a40e097..7e90fe3c98b23d00128237f0d403c6d545fbad63 100644 (file)
@@ -6,7 +6,10 @@ Group:      System/Utilities
 License:    Apache-2.0
 Source0:    %{name}-%{version}.tar.gz
 ExclusiveArch: %{arm}
+BuildRequires: cmake
 
+Requires: util-linux
+Requires: bash
 Requires: initrd-recovery
 
 %description
@@ -18,8 +21,14 @@ This package would be included in partition image for RAMDISK2 (ramdisk-recovery
 
 %build
 
+%cmake . -DCMAKE_INSTALL_PREFIX=%{_prefix}
+
+make %{?jobs:-j%jobs}
+
 %install
 
+%make_install
+
 %post
 
 %postun
@@ -27,3 +36,4 @@ This package would be included in partition image for RAMDISK2 (ramdisk-recovery
 %files
 %manifest initrd-flash.manifest
 %license LICENSE
+%attr(700,root,root) %{_bindir}/flash-manager
diff --git a/src/dfu.c b/src/dfu.c
new file mode 100644 (file)
index 0000000..77e47a7
--- /dev/null
+++ b/src/dfu.c
@@ -0,0 +1,373 @@
+/*
+ * flash-manager - Tizen kernel-level image flashing solution
+ *
+ * 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/wait.h>
+#include <sys/fcntl.h>
+#include <sys/mount.h>
+#include <pthread.h>
+
+#include "dfu.h"
+#include "thor-proto.h"
+
+static char *dfu_info[DFU_INFO_NUM][DFU_INFO_MAX] = {NULL,};
+
+void *dfu_get_buffer(unsigned long size)
+{
+       int wait = 100000; /* 100ms */
+       void *buf;
+
+       while (!(buf = malloc(size))) {
+               /*
+                * In the case of not enough memory, instead of returning error,
+                * transfer thread waits until dfu writes and frees buffers.
+                */
+               usleep(wait);
+               wait *= 2;
+       }
+
+       return buf;
+}
+
+void dfu_put_buffer(void *ptr)
+{
+       free(ptr);
+}
+
+static int find_match(const char *name)
+{
+       int i;
+
+       for (i = 0; i < DFU_INFO_NUM; i++) {
+               char *entry = dfu_info[i][DFU_INFO_NAME];
+
+               if (entry && !strncmp(entry, name, strlen(entry)))
+                       return i;
+       }
+
+       return -ENOENT;
+}
+
+int dfu_request_io(struct tfm_context *ctx, unsigned long len)
+{
+       int notify = 0;
+       struct dfu_frame *frame;
+
+       frame = malloc(sizeof(*frame));
+       if (!frame)
+               return -ENOMEM;
+
+       frame->buf = ctx->transfer_buffer;
+       frame->len = len;
+
+       pthread_mutex_lock(&ctx->dfu_mutex);
+
+       if (TAILQ_EMPTY(&ctx->dfu_ioq_head))
+               notify = 1;
+
+       /* dfu_thread_main() de-queues i/o request and processes it */
+       TAILQ_INSERT_TAIL(&ctx->dfu_ioq_head, frame, entry);
+
+       pthread_mutex_unlock(&ctx->dfu_mutex);
+
+       if (notify)
+               pthread_cond_signal(&ctx->dfu_data_arrive);
+
+       return 0;
+}
+
+static void mount_dev(const char *dev)
+{
+       int ret;
+
+       mkdir(DFU_MOUNT_PATH,  0600);
+
+       ret = mount(dev, DFU_MOUNT_PATH, "ext4", 0, NULL);
+       if (ret < 0) {
+               fprintf(stderr, "Failed to mount target partition\n");
+               rmdir(DFU_MOUNT_PATH);
+               exit(-1);
+       }
+}
+
+static void umount_dev(void)
+{
+       int ret;
+
+       ret = umount(DFU_MOUNT_PATH);
+       if (ret < 0) {
+               fprintf(stderr, "Failed to mount target partition\n");
+
+               /* umount is failed, but anyway try to delete mount point */
+               rmdir(DFU_MOUNT_PATH);
+               exit(-1);
+       }
+
+       rmdir(DFU_MOUNT_PATH);
+}
+
+void dfu_sync(struct tfm_context *ctx)
+{
+       char **info = ctx->dfu_info;
+
+       pthread_mutex_lock(&ctx->dfu_sync_mutex);
+       if (!ctx->transfer_done)
+               pthread_cond_wait(&ctx->dfu_write_done, &ctx->dfu_sync_mutex);
+       pthread_mutex_unlock(&ctx->dfu_sync_mutex);
+
+       switch (*info[DFU_INFO_MODE]) {
+       case 'f':
+               fsync(ctx->dfu_fd);
+               close(ctx->dfu_fd);
+               umount_dev();
+               break;
+       case 'p':
+               close(ctx->dfu_fd);
+               break;
+       default:
+               break;
+       }
+
+       pthread_cond_signal(&ctx->dfu_sync_done);
+
+       fprintf(stdout, "finished\n");
+}
+
+static int dfu_start_entity(struct tfm_context *ctx, int idx, unsigned long size)
+{
+       char **info = dfu_info[idx];
+       char *file;
+       int fd;
+
+       switch (*info[DFU_INFO_MODE]) {
+       case 'p':
+               file = info[DFU_INFO_DEV];
+               break;
+       case 'f':
+               mount_dev(info[DFU_INFO_DEV]);
+               file = info[DFU_INFO_PATH];
+               break;
+       default:
+               fprintf(stderr, "flash entry '%s' has wrong mode\n", info[DFU_INFO_NAME]);
+               return -EINVAL;
+       }
+
+       fd = open(file, O_WRONLY);
+       if (fd < 0) {
+               fprintf(stderr, "cannot open target: %s\n", info[DFU_INFO_NAME]);
+               return -EIO;
+       }
+
+       ctx->dfu_fd = fd;
+
+       ctx->dfu_info = info;
+       ctx->transfer_done = 0;
+
+       return 0;
+}
+
+
+int dfu_start(struct tfm_context *ctx, const char *entity)
+{
+       unsigned long size = ctx->thor_file_size;
+       int idx, ret;
+
+       idx = find_match(entity);
+       if (idx < 0) {
+               fprintf(stderr, "Cannot find dfu info for %s\n", entity);
+               return -EINVAL;
+       }
+
+       ret = dfu_start_entity(ctx, idx, size);
+       if (ret < 0) {
+               fprintf(stderr, "Cannot start download: %s\n", entity);
+               return -EINVAL;
+       }
+
+       fprintf(stdout, "Start download: %s...", entity);
+
+       return 0;
+}
+
+static void *dfu_thread_main(void *ptr)
+{
+       struct tfm_context *ctx = ptr;
+       struct dfu_frame *frame;
+       int error = 0;
+       uint64_t progress = 0;
+       int ret;
+
+       while (!error) {
+               pthread_mutex_lock(&ctx->dfu_mutex);
+
+               while (TAILQ_EMPTY(&ctx->dfu_ioq_head))
+                       pthread_cond_wait(&ctx->dfu_data_arrive, &ctx->dfu_mutex);
+
+               frame = TAILQ_FIRST(&ctx->dfu_ioq_head);
+
+               TAILQ_REMOVE(&ctx->dfu_ioq_head, frame, entry);
+
+               pthread_mutex_unlock(&ctx->dfu_mutex);
+
+               ret = write(ctx->dfu_fd, frame->buf, frame->len);
+
+               if (ret < frame->len) {
+                       fprintf(stdout, "Error occurs while flashing\n");
+                       error = 1;
+               }
+
+               progress += frame->len;
+
+               dfu_put_buffer(frame->buf);
+               free(frame);
+
+               /* transfer finished */
+               if (!error && progress >= ctx->thor_file_size) {
+                       progress = 0;
+                       ctx->transfer_done = 1;
+
+                       pthread_cond_signal(&ctx->dfu_write_done);
+
+                       pthread_mutex_lock(&ctx->dfu_sync_mutex);
+                       pthread_cond_wait(&ctx->dfu_sync_done,
+                                       &ctx->dfu_sync_mutex);
+                       pthread_mutex_unlock(&ctx->dfu_sync_mutex);
+               }
+       }
+
+       pthread_mutex_lock(&ctx->dfu_mutex);
+       while (TAILQ_EMPTY(&ctx->dfu_ioq_head)) {
+               frame = TAILQ_FIRST(&ctx->dfu_ioq_head);
+
+               TAILQ_REMOVE(&ctx->dfu_ioq_head, frame, entry);
+
+               dfu_put_buffer(frame->buf);
+               free(frame);
+       }
+       pthread_mutex_unlock(&ctx->dfu_mutex);
+
+       return NULL;
+}
+
+static int parse_dfu_info(char *buf, char **info)
+{
+       char *ptr;
+       int i;
+
+       for (i = 0; i < DFU_INFO_MAX; i++) {
+               ptr = strsep(&buf, DFU_DELIMITER);
+               if (!ptr)
+                       return -EINVAL;
+
+               info[i] = strdup(ptr);
+               if (!info[i])
+                       return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static void destroy_dfu_info(void)
+{
+       int i, j;
+
+       for (i = 0; i < DFU_INFO_NUM; i++) {
+               for (j = 0; j < DFU_INFO_MAX; j++)
+                       if (dfu_info[i][j]) {
+                               free(dfu_info[i][j]);
+                               dfu_info[i][j] = NULL;
+                       }
+       }
+}
+
+
+static int init_dfu_info(const char *dfu_info_file)
+{
+       FILE *fp;
+       char buf[1024];
+       int i = 0;
+       int ret;
+
+       fp = fopen(dfu_info_file, "r");
+       if (!fp)
+               return -ENOENT;
+
+       while (i < DFU_INFO_NUM && !feof(fp)) {
+               if (fgets(buf, 1024, fp) == NULL)
+                       break;
+
+               ret = parse_dfu_info(buf, dfu_info[i++]);
+               if (ret < 0) {
+                       fprintf(stderr, "cannot parse dfu info");
+                       goto err_free_all;
+               }
+       }
+
+       fclose(fp);
+
+       return 0;
+
+err_free_all:
+       fclose(fp);
+       destroy_dfu_info();
+
+       return ret;
+}
+
+
+int dfu_init(struct tfm_context *ctx, const char *dfu_info_file)
+{
+       int ret;
+
+       ret = init_dfu_info(dfu_info_file);
+       if (ret < 0) {
+               fprintf(stderr, "failed to get flash entries\n");
+               return ret;
+       }
+
+       TAILQ_INIT(&ctx->dfu_ioq_head);
+
+       pthread_mutex_init(&ctx->dfu_mutex, NULL);
+       pthread_mutex_init(&ctx->dfu_sync_mutex, NULL);
+       pthread_cond_init(&ctx->dfu_data_arrive, NULL);
+       pthread_cond_init(&ctx->dfu_write_done, NULL);
+       pthread_cond_init(&ctx->dfu_sync_done, NULL);
+
+       ret = pthread_create(&ctx->dfu_thread, NULL, dfu_thread_main, ctx);
+       if (ret < 0) {
+               fprintf(stderr, "failed to create thread for dfu\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+void dfu_exit(struct tfm_context *ctx)
+{
+       pthread_join(ctx->dfu_thread, NULL);
+       destroy_dfu_info();
+       if (ctx->connect) {
+               free(ctx->connect);
+               ctx->connect = NULL;
+       }
+}
diff --git a/src/dfu.h b/src/dfu.h
new file mode 100644 (file)
index 0000000..7f86a51
--- /dev/null
+++ b/src/dfu.h
@@ -0,0 +1,49 @@
+/*
+ * flash-manager - Tizen kernel-level image flashing solution
+ *
+ * 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 __DFU_H
+#define __DFU_H
+
+#include <sys/queue.h>
+#include "tfm.h"
+
+#define DFU_DELIMITER          " :,\t\n"
+#define DFU_INFO_NUM           100
+#define DFU_MOUNT_PATH         "/mnt/tfm-temp"
+
+enum dfu_info_entry {
+       DFU_INFO_MODE = 0,
+       DFU_INFO_NAME,
+       DFU_INFO_DEV,
+       DFU_INFO_PATH,
+       DFU_INFO_MAX,
+};
+
+struct dfu_frame {
+       void *buf;
+       unsigned long len;
+       TAILQ_ENTRY(dfu_frame) entry;
+};
+
+void *dfu_get_buffer(unsigned long size);
+void dfu_put_buffer(void *ptr);
+int dfu_init(struct tfm_context *ctx, const char *dfu_info_file);
+void dfu_exit(struct tfm_context *ctx);
+int dfu_start(struct tfm_context *ctx, const char *entity);
+int dfu_request_io(struct tfm_context *ctx, unsigned long len);
+void dfu_sync(struct tfm_context *ctx);
+
+#endif
diff --git a/src/main.c b/src/main.c
new file mode 100644 (file)
index 0000000..0b86091
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * flash-manager - Tizen kernel-level image flashing solution
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+#include "dfu.h"
+#include "net.h"
+
+int _main(int argc, char *argv[])
+{
+       struct tfm_context ctx;
+       const char *part_table = "/usr/share/partition.info";
+       int ret, opt;
+
+       memset(&ctx, 0, sizeof(ctx));
+
+       while ((opt = getopt(argc, argv, "p:i:")) != -1) {
+               switch (opt) {
+               case 'i':
+                       if (optarg) {
+                               part_table = strdup(optarg);
+                       } else {
+                               fprintf(stderr,
+                                       "path should be specified with '-i'\n");
+                               exit(-1);
+                       }
+                       break;
+               case 'p':
+               {
+                       unsigned long val;
+                       char *endptr = NULL;
+
+                       val = strtoul(optarg, &endptr, 0);
+                       if (*optarg == '\0' || (endptr && *endptr != '\0')) {
+                               fprintf(stderr,
+                                       "value should be provided as a number for '-p'\n");
+                               exit(-1);
+                       }
+                       ctx.port = (int)val;
+                       break;
+               }
+               default:
+                       return 0;
+               }
+       }
+
+       ret = dfu_init(&ctx, part_table);
+       if (ret < 0)
+               exit(-1);
+
+       ret = net_connect(&ctx);
+       if (ret < 0) {
+               dfu_exit(&ctx);
+               exit(-1);
+       }
+
+       ret = thor_init(&ctx);
+       if (ret < 0) {
+               net_disconnect(&ctx);
+               dfu_exit(&ctx);
+               exit(-1);
+       }
+
+       ret = thor_process(&ctx);
+       if (ret < 0) {
+               net_disconnect(&ctx);
+               dfu_exit(&ctx);
+               exit(-1);
+       }
+
+       net_disconnect(&ctx);
+       dfu_exit(&ctx);
+       exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+       pid_t pid;
+
+       for (;;) {
+               pid = fork();
+               if (pid < 0)
+                       exit(-1);
+
+               if (pid == 0)
+                       _main(argc, argv);
+
+               wait(NULL);
+
+               /*
+                * if control flow reaches here, flash might be failed.
+                * Thus, reset flash manager to accept other requests
+                */
+       }
+       return 0;
+}
diff --git a/src/net.c b/src/net.c
new file mode 100644 (file)
index 0000000..b946fa4
--- /dev/null
+++ b/src/net.c
@@ -0,0 +1,102 @@
+/*
+ * flash-manager - Tizen kernel-level image flashing solution
+ *
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+
+#include "net.h"
+
+static ssize_t net_rx_data(int sock, void *buf, ssize_t len)
+{
+       return recv(sock, buf, len, MSG_WAITALL);
+}
+
+static ssize_t net_tx_data(int sock, void *buf, ssize_t len)
+{
+       return send(sock, buf, len, 0);
+}
+
+int net_connect(struct tfm_context *ctx)
+{
+       struct sockaddr_in servaddr;
+       struct tfm_connect *conn;
+       int listener, sock;
+       int ret = 0;
+
+       conn = malloc(sizeof(*conn));
+       if (!conn)
+               return -ENOMEM;
+
+       listener = socket(AF_INET, SOCK_STREAM, 0);
+       if (listener < 0) {
+               fprintf(stderr, "Failed to create socket\n");
+               ret = -EINVAL;
+               goto err_free;
+       }
+
+       memset(&servaddr, 0, sizeof(servaddr));
+       servaddr.sin_family = AF_INET;
+       servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
+       servaddr.sin_port = htons((ctx->port ? ctx->port : DEFAULT_PORT));
+
+       ret = bind(listener, (struct sockaddr *) &servaddr, sizeof(servaddr));
+       if (ret < 0) {
+               fprintf(stderr, "Failed to bind socket\n");
+               goto err_socket;
+       }
+
+       ret = listen(listener, 1024);
+       if (ret < 0) {
+               fprintf(stderr, "Failed to call listen\n");
+               goto err_socket;
+       }
+
+       sock = accept(listener, NULL, NULL);
+       if (sock < 0) {
+               fprintf(stderr, "Failed to accept connection\n");
+               ret = sock;
+               goto err_socket;
+       }
+
+       conn->fd = sock;
+       conn->rx_data = net_rx_data;
+       conn->tx_data = net_tx_data;
+       ctx->connect = conn;
+
+       close(listener);
+
+       return 0;
+
+err_socket:
+       close(listener);
+err_free:
+       free(conn);
+       return ret;
+}
+
+void net_disconnect(struct tfm_context *ctx)
+{
+       close(ctx->connect->fd);
+
+       free(ctx->connect);
+       ctx->connect = NULL;
+}
diff --git a/src/net.h b/src/net.h
new file mode 100644 (file)
index 0000000..dc96b4d
--- /dev/null
+++ b/src/net.h
@@ -0,0 +1,27 @@
+/*
+ * flash-manager - Tizen kernel-level image flashing solution
+ *
+ * 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 __NET_H
+#define __NET_H
+
+#include "tfm.h"
+
+#define DEFAULT_PORT           23456
+
+int net_connect(struct tfm_context *ctx);
+void net_disconnect(struct tfm_context *ctx);
+
+#endif
diff --git a/src/tfm.h b/src/tfm.h
new file mode 100644 (file)
index 0000000..747de2d
--- /dev/null
+++ b/src/tfm.h
@@ -0,0 +1,54 @@
+/*
+ * flash-manager - Tizen kernel-level image flashing solution
+ *
+ * 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 __TFM_H
+#define __TFM_H
+
+#include <string.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <sys/queue.h>
+
+struct tfm_connect {
+       int fd;
+       ssize_t (*rx_data)(int fd, void *buf, ssize_t len);
+       ssize_t (*tx_data)(int fd, void *buf, ssize_t len);
+};
+
+struct tfm_context {
+       char **dfu_info;
+       int dfu_fd;
+       pthread_t dfu_thread;
+       pthread_mutex_t dfu_mutex;
+       pthread_mutex_t dfu_sync_mutex;
+       pthread_cond_t dfu_data_arrive;
+       pthread_cond_t dfu_write_done;
+       pthread_cond_t dfu_sync_done;
+       TAILQ_HEAD(tailhead, dfu_frame) dfu_ioq_head;
+
+       struct tfm_connect *connect;
+       uint64_t thor_file_size;
+       uint64_t remain;
+       void *transfer_buffer;
+       int transfer_done;
+
+       int port;
+};
+
+int thor_init(struct tfm_context *ctx);
+int thor_process(struct tfm_context *ctx);
+
+#endif
diff --git a/src/thor-proto.h b/src/thor-proto.h
new file mode 100644 (file)
index 0000000..f384b46
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * flash-manager - Tizen kernel-level image flashing solution
+ *
+ * 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 __THOR_PROTO_H__
+#define __THOR_PROTO_H__
+
+typedef enum {
+       RQT_INFO = 200,
+       RQT_CMD,
+       RQT_DL,
+       RQT_UL,
+} request_type;
+
+/* Request Data */
+/* RQT_INFO */
+enum {
+       RQT_INFO_VER_PROTOCOL = 1,
+       RQT_INFO_VER_HW,
+       RQT_INFO_VER_BOOT,
+       RQT_INFO_VER_KERNEL,
+       RQT_INFO_VER_PLATFORM,
+       RQT_INFO_VER_CSC,
+};
+
+/* RQT_CMD */
+enum {
+       RQT_CMD_REBOOT = 1,
+       RQT_CMD_POWEROFF,
+};
+
+/* RQT_DL */
+enum {
+       RQT_DL_INIT = 1,
+       RQT_DL_FILE_INFO,
+       RQT_DL_FILE_START,
+       RQT_DL_FILE_END,
+       RQT_DL_EXIT,
+};
+
+/* RQT_UL */
+enum {
+       RQT_UL_INIT = 1,
+       RQT_UL_START,
+       RQT_UL_END,
+       RQT_UL_EXIT,
+};
+
+enum __binary_type {
+       BINARY_TYPE_NORMAL = 0,
+       BINARY_TYPE_PIT,
+};
+
+struct rqt_pkt {
+       int32_t id;                     /* Request Group ID. */
+       int32_t sub_id;                 /* Request Data ID. */
+       int32_t int_data[14];           /* Int. Datas. */
+       char    str_data[5][32];        /* Str. Data. */
+       char    md5[32];                /* MD5 Checksum. */
+} __attribute__((__packed__));
+
+struct res_pkt {
+       int32_t id;                     /* Response Group ID == Request Group ID. */
+       int32_t sub_id;                 /* Response Data ID == Request Data ID. */
+       int32_t ack;                    /* Ack. */
+       int32_t int_data[5];            /* Int. Datas. */
+       char    str_data[3][32];        /* Str. Data. */
+} __attribute__((__packed__));
+
+struct data_res_pkt {
+       int32_t ack;                    /* Ack. */
+       int32_t cnt;                    /* Int. Datas. */
+} __attribute__((__packed__));
+
+#define VER_PROTOCOL_MAJOR     4
+#define VER_PROTOCOL_MINOR     0
+
+#define RQT_PKT_SIZE           sizeof(struct rqt_pkt)
+#define RES_PKT_SIZE           sizeof(struct res_pkt)
+#define DATA_RES_PKT_SIZE      sizeof(struct data_res_pkt)
+
+#define FILE_NAME_MAXLEN       32
+#define DATA_PKT_SIZE          0x00100000      /* 1 MiB */
+#define FLASH_UNIT_SIZE                0x02000000      /* 32 MiB */
+
+
+#endif /* __THOR_PROTO_H__ */
diff --git a/src/thor.c b/src/thor.c
new file mode 100644 (file)
index 0000000..f12dfc3
--- /dev/null
@@ -0,0 +1,343 @@
+/*
+ * flash-manager - Tizen kernel-level image flashing solution
+ *
+ * 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 <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <stdlib.h>
+#include <sys/reboot.h>
+
+#include "dfu.h"
+#include "thor-proto.h"
+
+static inline ssize_t thor_rx_data(struct tfm_connect *conn, void *buf, ssize_t len)
+{
+       return conn->rx_data(conn->fd, buf, len);
+}
+
+static inline ssize_t thor_tx_data(struct tfm_connect *conn, void *buf, ssize_t len)
+{
+       return conn->tx_data(conn->fd, buf, len);
+}
+
+static unsigned int _checkboard(void)
+{
+       /*
+        * TODO:
+        * Add support to get proper device name
+        */
+       return -1;
+}
+
+static int thor_send_rsp(struct tfm_connect *conn, const struct res_pkt *rsp)
+{
+       ssize_t n;
+
+       n = thor_tx_data(conn, (void *)rsp, RES_PKT_SIZE);
+       if (n < sizeof(*rsp))
+               return -EIO;
+
+       return 0;
+}
+
+static int thor_process_rqt_info(struct tfm_context *ctx, struct rqt_pkt *rqt)
+{
+       struct res_pkt rsp;
+       int ret;
+
+       memset(&rsp, 0, RES_PKT_SIZE);
+
+       rsp.id = rqt->id;
+       rsp.sub_id = rqt->sub_id;
+
+       switch (rqt->sub_id) {
+       case RQT_INFO_VER_PROTOCOL:
+               rsp.int_data[0] = VER_PROTOCOL_MAJOR;
+               rsp.int_data[1] = VER_PROTOCOL_MINOR;
+               break;
+       case RQT_INFO_VER_HW:
+               snprintf(rsp.str_data[0], sizeof(rsp.str_data[0]), "%x", _checkboard());
+               break;
+       case RQT_INFO_VER_BOOT:
+       case RQT_INFO_VER_KERNEL:
+       case RQT_INFO_VER_PLATFORM:
+       case RQT_INFO_VER_CSC:
+               strncpy(rsp.str_data[0], "Unknown", sizeof("Unknown"));
+               break;
+       default:
+               fprintf(stderr, "Not supported information request: %d\n",
+                                                               rqt->sub_id);
+               return -EINVAL;
+       }
+
+       ret = thor_send_rsp(ctx->connect, &rsp);
+       if (ret < 0) {
+               fprintf(stderr, "failed to send response of REQUEST_INFO\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int thor_process_rqt_cmd(struct tfm_context *ctx, struct rqt_pkt *rqt)
+{
+       struct tfm_connect *conn = ctx->connect;
+       struct res_pkt rsp;
+       int ret;
+
+       memset(&rsp, 0, RES_PKT_SIZE);
+
+       rsp.id = rqt->id;
+       rsp.sub_id = rqt->sub_id;
+
+       switch (rqt->sub_id) {
+       case RQT_CMD_REBOOT:
+               thor_send_rsp(conn, &rsp);
+
+               ret = reboot(RB_AUTOBOOT);
+               if (ret < 0) {
+                       fprintf(stderr, "Failed to system reboot\n");
+                       return -EINVAL;
+               }
+               break;
+       case RQT_CMD_POWEROFF:
+               thor_send_rsp(conn, &rsp);
+               break;
+       default:
+               fprintf(stderr, "Not supported command request: %d",
+                                                               rqt->sub_id);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void thor_send_data_rsp(struct tfm_connect *conn, int ack, int count)
+{
+       struct data_res_pkt rsp;
+
+       rsp.ack = ack;
+       rsp.cnt = count;
+
+       thor_tx_data(conn, &rsp, DATA_RES_PKT_SIZE);
+}
+
+static int thor_download_head(struct tfm_context *ctx, unsigned int packet_size)
+{
+       struct tfm_connect *conn = ctx->connect;
+       uint64_t recv = 0;
+       uint64_t total = ctx->thor_file_size;
+       void *buf;
+       int usb_pkt_cnt = 0, n;
+       int ret;
+
+       ctx->transfer_buffer = buf = dfu_get_buffer(FLASH_UNIT_SIZE);
+
+       while (total - recv >= packet_size) {
+               n = thor_rx_data(conn, buf, packet_size);
+
+               if (n < packet_size)
+                       return -EIO;
+
+               recv += n;
+               buf += packet_size;
+
+               if ((recv % FLASH_UNIT_SIZE) == 0) {
+                       ret = dfu_request_io(ctx, FLASH_UNIT_SIZE);
+                       if (ret < 0)
+                               return -EIO;
+
+                       ctx->transfer_buffer = buf = dfu_get_buffer(FLASH_UNIT_SIZE);
+               }
+               thor_send_data_rsp(conn, 0, ++usb_pkt_cnt);
+       }
+
+       ctx->remain = (total - recv) + buf - ctx->transfer_buffer;
+
+       if ((total - recv) > 0) {
+               n = thor_rx_data(conn, buf, packet_size);
+               if (n < packet_size)
+                       return -EIO;
+               recv += n;
+               thor_send_data_rsp(conn, 0, ++usb_pkt_cnt);
+       }
+
+       return 0;
+}
+
+static int thor_download_tail(struct tfm_context *ctx)
+{
+       int ret;
+
+       if (ctx->remain) {
+               ret = dfu_request_io(ctx, ctx->remain);
+               if (ret < 0)
+                       return -EIO;
+       } else {
+               /* if there is no remain, buffer should be freed */
+               dfu_put_buffer(ctx->transfer_buffer);
+       }
+
+       dfu_sync(ctx);
+
+       return 0;
+}
+
+static int thor_process_rqt_download(struct tfm_context *ctx, struct rqt_pkt *rqt)
+{
+       struct tfm_connect *conn = ctx->connect;
+       char f_name[FILE_NAME_MAXLEN + 1] = {0,};
+       struct res_pkt rsp;
+       int ret = 0, head, file_type;
+
+       memset(&rsp, 0, RES_PKT_SIZE);
+
+       rsp.id = rqt->id;
+       rsp.sub_id = rqt->sub_id;
+
+       switch (rqt->sub_id) {
+       case RQT_DL_INIT:
+               ctx->thor_file_size = (unsigned long)rqt->int_data[0];
+               break;
+       case RQT_DL_FILE_INFO:
+               file_type = rqt->int_data[0];
+               if (file_type != BINARY_TYPE_NORMAL) {
+                       fprintf(stderr, "Currently only NORMAL_FILE is supported\n");
+                       ret = rsp.ack = -EINVAL;
+                       break;
+               }
+
+               ctx->thor_file_size = (unsigned long)rqt->int_data[1];
+               memcpy(f_name, rqt->str_data[0], FILE_NAME_MAXLEN);
+
+               rsp.int_data[0] = DATA_PKT_SIZE;
+
+               ret = dfu_start(ctx, f_name);
+               if (ret < 0) {
+                       fprintf(stderr, "failed to start dfu\n");
+                       ret = -EINVAL;
+               }
+               break;
+       case RQT_DL_FILE_START:
+               ret = thor_send_rsp(conn, &rsp);
+               if (ret < 0) {
+                       fprintf(stderr, "failed to send response\n");
+                       ret = rsp.ack = -EINVAL;
+                       break;
+               }
+               head = thor_download_head(ctx, DATA_PKT_SIZE);
+               if (head < 0)
+                       ctx->remain = 0;
+
+               return head;
+       case RQT_DL_FILE_END:
+               rsp.ack = thor_download_tail(ctx);
+               ret = rsp.ack;
+               ctx->remain = 0;
+               break;
+       case RQT_DL_EXIT:
+               break;
+       default:
+               fprintf(stderr, "Invalid download request: %d\n",
+                                                               rqt->sub_id);
+               ret = -EINVAL;
+       }
+
+       thor_send_rsp(conn, &rsp);
+       return ret;
+}
+
+static int thor_do_request(struct tfm_context *ctx, struct rqt_pkt *rqt)
+{
+       int ret;
+
+       switch (rqt->id) {
+       case RQT_INFO:
+               ret = thor_process_rqt_info(ctx, rqt);
+               break;
+       case RQT_CMD:
+               ret = thor_process_rqt_cmd(ctx, rqt);
+               break;
+       case RQT_DL:
+               ret = thor_process_rqt_download(ctx, rqt);
+               break;
+       case RQT_UL:
+               fprintf(stderr, "Request \"UPLOAD\" is not supported\n");
+               ret = -EINVAL;
+               break;
+       default:
+               fprintf(stderr, "Invalid request in rqt: %d\n", rqt->id);
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+static int thor_handshake(struct tfm_connect *conn)
+{
+       char buf[5];
+       ssize_t n;
+
+       n = thor_rx_data(conn, buf, 4/* strlen("THOR") */);
+       if (n < 4)
+               return -EIO;
+
+       if (!strncmp(buf, "THOR", 4)) {
+               n = thor_tx_data(conn, "ROHT", 4);
+               if (n < 4)
+                       return -EIO;
+       } else {
+               fprintf(stderr, "Invalid request from the host\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+int thor_init(struct tfm_context *ctx)
+{
+       return thor_handshake(ctx->connect);
+}
+
+int thor_process(struct tfm_context *ctx)
+{
+       struct tfm_connect *conn = ctx->connect;
+       struct rqt_pkt rqt;
+       ssize_t n;
+       int ret;
+
+       for (;;) {
+               n = thor_rx_data(conn, &rqt, RQT_PKT_SIZE);
+               if (n < sizeof(rqt)) {
+                       fprintf(stderr,
+                               "Failed to receive data from the host(%ld:%ld)",
+                               n, RQT_PKT_SIZE);
+                       return -EIO;
+               }
+
+               ret = thor_do_request(ctx, &rqt);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return 0;
+}