From: Dongwoo Lee Date: Thu, 2 Nov 2017 21:33:17 +0000 (+0900) Subject: Add initial implementation of network backend X-Git-Tag: submit/devel/20180917.094703~3 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=caa9abb4845e7fb22cb7169ed5cbb0792de8ac50;p=platform%2Fcore%2Fsystem%2Finitrd-flash.git Add initial implementation of network backend 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 --- diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..8688aa1 --- /dev/null +++ b/CMakeLists.txt @@ -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) diff --git a/packaging/initrd-flash.spec b/packaging/initrd-flash.spec index 2a3486b..7e90fe3 100644 --- a/packaging/initrd-flash.spec +++ b/packaging/initrd-flash.spec @@ -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 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 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 +#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 index 0000000..0b86091 --- /dev/null +++ b/src/main.c @@ -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 +#include +#include +#include +#include + +#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 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 +#include +#include +#include +#include +#include +#include +#include + +#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 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 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 +#include +#include +#include + +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 index 0000000..f384b46 --- /dev/null +++ b/src/thor-proto.h @@ -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 index 0000000..f12dfc3 --- /dev/null +++ b/src/thor.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +}