--- /dev/null
+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)
License: Apache-2.0
Source0: %{name}-%{version}.tar.gz
ExclusiveArch: %{arm}
+BuildRequires: cmake
+Requires: util-linux
+Requires: bash
Requires: initrd-recovery
%description
%build
+%cmake . -DCMAKE_INSTALL_PREFIX=%{_prefix}
+
+make %{?jobs:-j%jobs}
+
%install
+%make_install
+
%post
%postun
%files
%manifest initrd-flash.manifest
%license LICENSE
+%attr(700,root,root) %{_bindir}/flash-manager
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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;
+}