From 19f856576e42745cf8a1d2256c3b6cb54e4263eb Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Thu, 9 May 2019 16:10:40 +0900 Subject: [PATCH 01/16] scripts: flash-init: create symbolic link about firmware path Create symbolic link about firmware path. Change-Id: Ia0243afbafddbeef2bf797b3ad184150af75f4c5 Signed-off-by: Jaehoon Chung --- scripts/41-flash.list | 1 + scripts/flash-init.sh | 2 ++ 2 files changed, 3 insertions(+) diff --git a/scripts/41-flash.list b/scripts/41-flash.list index 66a2c69..c9c2611 100755 --- a/scripts/41-flash.list +++ b/scripts/41-flash.list @@ -26,6 +26,7 @@ WITHLIBS=" /bin/find /bin/hostapd /bin/dnsmasq +/bin/ln " # LinkFileName:Target diff --git a/scripts/flash-init.sh b/scripts/flash-init.sh index e5d12f8..799e80a 100755 --- a/scripts/flash-init.sh +++ b/scripts/flash-init.sh @@ -18,6 +18,7 @@ FIND="/usr/bin/find" INSMOD="/usr/sbin/insmod" DNSMASQ="/usr/bin/dnsmasq" HOSTAPD="/usr/bin/hostapd" +LINK="/usr/bin/ln" NETIF="eth0" WLANIF="wlan0" @@ -44,6 +45,7 @@ setup_wireless() { BRCMUTIL=`$FIND ${TMP_PATH} -name brcmutil.ko` echo "Module brcmfmac is $BRCMFMAC" echo "Module brcmutil is $BRCMUTIL" + $LINK -s /usr/share/firmware /lib/firmware $COPY $BRCMFMAC /lib/firmware/brcm/ $COPY $BRCMUTIL /lib/firmware/brcm/ $SYNC -- 2.7.4 From b45ae10a1ffaa0ee18581c655c0b4117d25eba69 Mon Sep 17 00:00:00 2001 From: Junghoon Kim Date: Thu, 9 May 2019 21:26:39 +0900 Subject: [PATCH 02/16] data: Update the partition info file to support 32-bit architecture This patch adds the dtb and zImage to the partition info file, in order to support 32-bit architecture. Also, common u-boot and config files have been added to facilitate testing between 32-bit and 64-bit architectures. Change-Id: Idf27f33e50bbafbf438b78d6b336d4794416aa18 Signed-off-by: Junghoon Kim --- data/partition_rpi3.info | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/data/partition_rpi3.info b/data/partition_rpi3.info index 8acd369..e22defc 100644 --- a/data/partition_rpi3.info +++ b/data/partition_rpi3.info @@ -6,7 +6,13 @@ p:/dev/mmcblk0p6:modules.img:x p:/dev/mmcblk0p7:ramdisk.img:x p:/dev/mmcblk0p8:ramdisk-recovery.img:x f:/dev/mmcblk0p1:Image:/ +f:/dev/mmcblk0p1:zImage:/ f:/dev/mmcblk0p1:bcm2710-rpi-3-b-plus.dtb:/ f:/dev/mmcblk0p1:bcm2710-rpi-cm3.dtb:/ f:/dev/mmcblk0p1:bcm2710-rpi-3-b.dtb:/ f:/dev/mmcblk0p1:bcm2837-rpi-3-b.dtb:/ +f:/dev/mmcblk0p1:bcm2837-rpi-3-b-plus.dtb:/ +f:/dev/mmcblk0p1:u-boot.bin:/ +f:/dev/mmcblk0p1:uboot.env:/ +f:/dev/mmcblk0p1:boot.scr.uimg:/ +f:/dev/mmcblk0p1:config.txt:/ -- 2.7.4 From ad730409dcecc9e34e7b5ecc2da459a43640854b Mon Sep 17 00:00:00 2001 From: Dongwoo Lee Date: Tue, 25 Jun 2019 10:23:49 +0900 Subject: [PATCH 03/16] CMakeLists: Apply PIE flags To support ASLR, this patch applies PIE (position independent executable) flags to CMakeLists.txt. Change-Id: If7eae9c8aad08fe9da3083cbdad64a7221fdaa98 Signed-off-by: Dongwoo Lee --- CMakeLists.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ca1de19..c722f06 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,9 @@ CMAKE_MINIMUM_REQUIRED(VERSION 2.6) PROJECT(flash-manager C) +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -fPIE -O2") +SET(CMAKE_EXE_LINKER_FLAGS "-pie") + FIND_PACKAGE(Threads REQUIRED) ADD_EXECUTABLE(${PROJECT_NAME} src/main.c src/thor.c src/dfu.c src/net.c) @@ -9,6 +12,4 @@ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/src) TARGET_LINK_LIBRARIES(flash-manager "${CMAKE_THREAD_LIBS_INIT}" blkid) -ADD_DEFINITIONS(-Wall -g -O2) - INSTALL(TARGETS ${PROJECT_NAME} DESTINATION bin) -- 2.7.4 From 4162a2d539e6d3a52521c3cff9b0960db5a954ca Mon Sep 17 00:00:00 2001 From: Dongwoo Lee Date: Mon, 8 Jul 2019 16:07:45 +0900 Subject: [PATCH 04/16] dfu: Free resources on error This patch makes resources are freed on error case while scanning the type of filesystem with blkid APIs. This resource leakage was reported by static analystic tool. Change-Id: Ibeba4190316fb1db0bde02eaca0648c0db0bad2e Signed-off-by: Dongwoo Lee --- src/dfu.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/dfu.c b/src/dfu.c index 0131e42..4edf1a1 100644 --- a/src/dfu.c +++ b/src/dfu.c @@ -102,7 +102,7 @@ static char *get_partition_fstype(const char *devname) blkid_tag_iterate tag_iter; blkid_dev dev; blkid_cache cache = NULL; - const char *type, *value; + const char *type, *value, *fstype = NULL; int ret; ret = blkid_get_cache(&cache, NULL); @@ -113,29 +113,29 @@ static char *get_partition_fstype(const char *devname) dev = blkid_get_dev(cache, devname, 0); if (!dev) - return NULL; + goto err_put_cache; dev = blkid_verify(cache, dev); if (!dev) - return NULL; + goto err_put_cache; tag_iter = blkid_tag_iterate_begin(dev); while (blkid_tag_next(tag_iter, &type, &value) == 0) { if (!strncmp(type, "TYPE", 4)) { - char *fstype = strdup(value); - + fstype = strdup(value); if (!fstype) - return NULL; + fprintf(stderr, "failed to duplicate fs type string\n"); - blkid_tag_iterate_end(tag_iter); - blkid_put_cache(cache); - return fstype; + break; } } + blkid_tag_iterate_end(tag_iter); + +err_put_cache: blkid_put_cache(cache); - return NULL; + return fstype; } static int mount_dev(const char *dev) -- 2.7.4 From dc56768ae46d1076abe20e03c66fd3d99bf49dc2 Mon Sep 17 00:00:00 2001 From: Dongwoo Lee Date: Fri, 3 Jan 2020 14:46:28 +0900 Subject: [PATCH 05/16] data: Add rpi4 dtb as a flashing entity To support tizen flash manager for rpi4, this adds a dtb file for bcm2711 into partition information file. Change-Id: I9b4a97c5d80f40c329d04f87d1eb1b29066d17f0 Signed-off-by: Dongwoo Lee --- data/partition_rpi3.info | 1 + 1 file changed, 1 insertion(+) diff --git a/data/partition_rpi3.info b/data/partition_rpi3.info index e22defc..ad1bb42 100644 --- a/data/partition_rpi3.info +++ b/data/partition_rpi3.info @@ -12,6 +12,7 @@ f:/dev/mmcblk0p1:bcm2710-rpi-cm3.dtb:/ f:/dev/mmcblk0p1:bcm2710-rpi-3-b.dtb:/ f:/dev/mmcblk0p1:bcm2837-rpi-3-b.dtb:/ f:/dev/mmcblk0p1:bcm2837-rpi-3-b-plus.dtb:/ +f:/dev/mmcblk0p1:bcm2711-rpi-4-b.dtb:/ f:/dev/mmcblk0p1:u-boot.bin:/ f:/dev/mmcblk0p1:uboot.env:/ f:/dev/mmcblk0p1:boot.scr.uimg:/ -- 2.7.4 From 2b507f419bfe606103e1f680af31b9838f8f919d Mon Sep 17 00:00:00 2001 From: Dongwoo Lee Date: Wed, 8 Jan 2020 17:53:40 +0900 Subject: [PATCH 06/16] Fix gcc9 build warnings This fixes build warnings on compiling with gcc9. Change-Id: I7ff58499fc2813931ff49c8f7d58e851b08ef5dc Signed-off-by: Dongwoo Lee --- src/dfu.c | 3 ++- src/thor.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/dfu.c b/src/dfu.c index 4edf1a1..ea548f5 100644 --- a/src/dfu.c +++ b/src/dfu.c @@ -102,7 +102,8 @@ static char *get_partition_fstype(const char *devname) blkid_tag_iterate tag_iter; blkid_dev dev; blkid_cache cache = NULL; - const char *type, *value, *fstype = NULL; + const char *type, *value; + char *fstype = NULL; int ret; ret = blkid_get_cache(&cache, NULL); diff --git a/src/thor.c b/src/thor.c index 1d131cf..d6dfa09 100644 --- a/src/thor.c +++ b/src/thor.c @@ -81,7 +81,7 @@ static int thor_process_rqt_info(struct tfm_context *ctx, struct rqt_pkt *rqt) case RQT_INFO_VER_KERNEL: case RQT_INFO_VER_PLATFORM: case RQT_INFO_VER_CSC: - strncpy(rsp.str_data[0], "Unknown", sizeof("Unknown")); + strncpy(rsp.str_data[0], "Unknown", 32); break; default: fprintf(stderr, "Not supported information request: %d\n", -- 2.7.4 From 7062cae242638f20b61cda27f10a67b54879e427 Mon Sep 17 00:00:00 2001 From: Seung-Woo Kim Date: Fri, 10 Jan 2020 15:10:49 +0900 Subject: [PATCH 07/16] dfu: do stringop with termination character Doing string operation with strlen() result, there is possibilty of omission of termination character. Do stringop with termination character with strlen() + 1. The destination buffer is allocated sum of all strlen() and 1, there is no overflow. Change-Id: I35627dbbb0ac72bc58398ccfffa43802729c5b85 Signed-off-by: Seung-Woo Kim --- src/dfu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dfu.c b/src/dfu.c index ea548f5..0a1b297 100644 --- a/src/dfu.c +++ b/src/dfu.c @@ -236,8 +236,8 @@ static int dfu_start_entity(struct tfm_context *ctx, int idx, unsigned long size } strncpy(file, DFU_MOUNT_PATH, path_prefix + 1); - strncat(file, info[DFU_INFO_PATH], path_suffix); - strncat(file, info[DFU_INFO_NAME], path_name); + strncat(file, info[DFU_INFO_PATH], path_suffix + 1); + strncat(file, info[DFU_INFO_NAME], path_name + 1); break; } default: -- 2.7.4 From 47fba2f480c071a3b308910dbd47e6aa2b49cb49 Mon Sep 17 00:00:00 2001 From: Seung-Woo Kim Date: Fri, 10 Jan 2020 15:29:42 +0900 Subject: [PATCH 08/16] fix use string after null check The result of strdup() can be null, so use it after null check. Change-Id: I61b5e5a4d4b631984eb19240b46afe0e94752043 Signed-off-by: Seung-Woo Kim --- src/main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index 194e2c8..80267db 100644 --- a/src/main.c +++ b/src/main.c @@ -39,7 +39,8 @@ int _main(int argc, char *argv[]) if (opt_table) free(opt_table); opt_table = strdup(optarg); - part_table = opt_table; + if (opt_table) + part_table = opt_table; } else { fprintf(stderr, "path should be specified with '-i'\n"); -- 2.7.4 From 737d065c7fa0dc71223f6eb83dcb938b640dbea4 Mon Sep 17 00:00:00 2001 From: Seung-Woo Kim Date: Mon, 10 Feb 2020 15:28:36 +0900 Subject: [PATCH 09/16] Fix to check memory allocation failure The strdup() can be failed and return NULL. Not to access wrong memory, explictly check memory allocation failure. Change-Id: I2917cf0cebd6488004b653c29137c0acbb56074e Signed-off-by: Seung-Woo Kim --- src/main.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index 80267db..32c2473 100644 --- a/src/main.c +++ b/src/main.c @@ -39,8 +39,13 @@ int _main(int argc, char *argv[]) if (opt_table) free(opt_table); opt_table = strdup(optarg); - if (opt_table) + if (opt_table) { part_table = opt_table; + } else { + fprintf(stderr, "Out of memory\n"); + ret = -1; + goto out; + } } else { fprintf(stderr, "path should be specified with '-i'\n"); -- 2.7.4 From 29950f08d56bd278a198463489be60d6bbdba49c Mon Sep 17 00:00:00 2001 From: Dongwoo Lee Date: Wed, 18 Mar 2020 12:35:59 +0900 Subject: [PATCH 10/16] thor: Bump protocol version to 5.0 To support the images exceeds 4GB, thor protocol 5.0 is applied. This requires thor client support: Ref: https://git.tizen.org/cgit/tools/lthor/commit/?id=30fa9f0cbfc4b089bb7e733b9cb75a60e1d10369 u-boot thor downloader already supports protocol 5.0: Ref: https://gitlab.denx.de/u-boot/u-boot/-/commit/1fe9ae76b113103bcc40aa15949f9dd8aa0a06a2 Change-Id: I149361ca2f40b5775249f3672e3e19b2f6394b68 Signed-off-by: Dongwoo Lee --- src/thor-proto.h | 2 +- src/thor.c | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/thor-proto.h b/src/thor-proto.h index f384b46..c26aefd 100644 --- a/src/thor-proto.h +++ b/src/thor-proto.h @@ -84,7 +84,7 @@ struct data_res_pkt { int32_t cnt; /* Int. Datas. */ } __attribute__((__packed__)); -#define VER_PROTOCOL_MAJOR 4 +#define VER_PROTOCOL_MAJOR 5 #define VER_PROTOCOL_MINOR 0 #define RQT_PKT_SIZE sizeof(struct rqt_pkt) diff --git a/src/thor.c b/src/thor.c index d6dfa09..e19d33f 100644 --- a/src/thor.c +++ b/src/thor.c @@ -216,7 +216,8 @@ static int thor_process_rqt_download(struct tfm_context *ctx, struct rqt_pkt *rq switch (rqt->sub_id) { case RQT_DL_INIT: - ctx->thor_file_size = (unsigned long)rqt->int_data[0]; + ctx->thor_file_size = (uint64_t)rqt->int_data[0] + + ((uint64_t)rqt->int_data[1] << 32); break; case RQT_DL_FILE_INFO: file_type = rqt->int_data[0]; @@ -226,7 +227,8 @@ static int thor_process_rqt_download(struct tfm_context *ctx, struct rqt_pkt *rq break; } - ctx->thor_file_size = (unsigned long)rqt->int_data[1]; + ctx->thor_file_size = (uint64_t)rqt->int_data[1] + + ((uint64_t)rqt->int_data[2] << 32); memcpy(f_name, rqt->str_data[0], FILE_NAME_MAXLEN); rsp.int_data[0] = DATA_PKT_SIZE; -- 2.7.4 From bdeb2d6876465d4d3b38b91f0f06b9a993dc5f59 Mon Sep 17 00:00:00 2001 From: Dongwoo Lee Date: Tue, 17 Mar 2020 13:29:24 +0900 Subject: [PATCH 11/16] tfm: Refactor code structure To cleanup code and resolve dependency on each module, this just refactors code structure without functional changes. Change-Id: I0c9e4b50d1054255c4f33f2a0218469c0647d2c7 Signed-off-by: Dongwoo Lee --- src/dfu.c | 408 ++++++++++++++++++++++++++++++++++--------------------------- src/dfu.h | 46 +++---- src/main.c | 27 ++-- src/tfm.h | 22 +--- src/thor.c | 170 ++++++++++++++++---------- 5 files changed, 370 insertions(+), 303 deletions(-) diff --git a/src/dfu.c b/src/dfu.c index 0a1b297..4ae9145 100644 --- a/src/dfu.c +++ b/src/dfu.c @@ -31,7 +31,44 @@ #include "dfu.h" #include "thor-proto.h" -static char *dfu_info[DFU_INFO_NUM][DFU_INFO_MAX] = {NULL,}; +#define DFU_DELIMITER " :,\t\n" +#define DFU_ENTRY_LIST_MAXLEN 100 +#define DFU_MOUNT_PATH "/mnt/tfm-temp" + +#define DFU_INFO_MODE_PARTITION 'p' +#define DFU_INFO_MODE_FILE 'f' + +enum dfu_entry_attr { + DFU_INFO_MODE = 0, + DFU_INFO_PARTITION, + DFU_INFO_NAME, + DFU_INFO_PATH, + DFU_INFO_MAX, +}; + +enum dfu_thread_state { + DFU_THREAD_STATE_ERROR = -1, + DFU_THREAD_STATE_IDLE, + DFU_THREAD_STATE_FLASHING, +}; + +struct dfu_frame { + void *buf; + unsigned long len; + TAILQ_ENTRY(dfu_frame) entry; +}; + +struct dfu_context { + pthread_t thread; + pthread_mutex_t mutex; + pthread_mutex_t sync_mutex; + pthread_cond_t data_arrived; + pthread_cond_t write_done; + pthread_cond_t sync_done; + TAILQ_HEAD(tailhead, dfu_frame) ioq; + + char *dfu_entry_list[DFU_ENTRY_LIST_MAXLEN][DFU_INFO_MAX]; +}; void *dfu_get_buffer(unsigned long size) { @@ -55,44 +92,45 @@ void dfu_put_buffer(void *ptr) free(ptr); } -static int find_match(const char *name) +static char **find_entry_match(struct dfu_context *ctx, const char *name) { int i; - for (i = 0; i < DFU_INFO_NUM; i++) { - char *entry = dfu_info[i][DFU_INFO_NAME]; + for (i = 0; i < DFU_ENTRY_LIST_MAXLEN; i++) { + char *entry = ctx->dfu_entry_list[i][DFU_INFO_NAME]; if (entry && !strncmp(entry, name, strlen(entry))) - return i; + return ctx->dfu_entry_list[i]; } - return -ENOENT; + return NULL; } -int dfu_request_io(struct tfm_context *ctx, unsigned long len) +int dfu_request_io(struct dfu_entry *e, void *buffer, unsigned long len) { - int notify = 0; + struct dfu_context *ctx = e->ctx; struct dfu_frame *frame; + int notify = 0; frame = malloc(sizeof(*frame)); if (!frame) return -ENOMEM; - frame->buf = ctx->transfer_buffer; + frame->buf = buffer; frame->len = len; - pthread_mutex_lock(&ctx->dfu_mutex); + pthread_mutex_lock(&ctx->mutex); - if (TAILQ_EMPTY(&ctx->dfu_ioq_head)) + if (TAILQ_EMPTY(&ctx->ioq)) notify = 1; /* dfu_thread_main() de-queues i/o request and processes it */ - TAILQ_INSERT_TAIL(&ctx->dfu_ioq_head, frame, entry); + TAILQ_INSERT_TAIL(&ctx->ioq, frame, entry); - pthread_mutex_unlock(&ctx->dfu_mutex); + pthread_mutex_unlock(&ctx->mutex); if (notify) - pthread_cond_signal(&ctx->dfu_data_arrive); + pthread_cond_signal(&ctx->data_arrived); return 0; } @@ -179,53 +217,112 @@ static void umount_dev(void) rmdir(DFU_MOUNT_PATH); } -void dfu_sync(struct tfm_context *ctx) +static void dfu_thread_cleanup(void *ptr) { - char **info = ctx->dfu_info; + struct dfu_entry *e = ptr; + struct dfu_context *ctx = e->ctx; + struct dfu_frame *frame; - 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); + while (!TAILQ_EMPTY(&ctx->ioq)) { + frame = TAILQ_FIRST(&ctx->ioq); - switch (*info[DFU_INFO_MODE]) { - case DFU_INFO_MODE_FILE: - fsync(ctx->dfu_fd); - close(ctx->dfu_fd); - umount_dev(); - break; - case DFU_INFO_MODE_PARTITION: - close(ctx->dfu_fd); - break; - default: - break; + TAILQ_REMOVE(&ctx->ioq, frame, entry); + + dfu_put_buffer(frame->buf); + free(frame); } - pthread_cond_signal(&ctx->dfu_sync_done); + free(e); +} - fprintf(stdout, "finished\n"); +static void *dfu_thread_main(void *ptr) +{ + struct dfu_entry *e = ptr; + struct dfu_context *ctx = e->ctx; + struct dfu_frame *frame; + int state = DFU_THREAD_STATE_IDLE; + uint64_t progress = 0; + int ret; + + pthread_cleanup_push(dfu_thread_cleanup, ptr); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + + while (state != DFU_THREAD_STATE_ERROR) { + pthread_mutex_lock(&ctx->mutex); + + while (TAILQ_EMPTY(&ctx->ioq)) { + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + state = DFU_THREAD_STATE_IDLE; + pthread_cond_wait(&ctx->data_arrived, &ctx->mutex); + } + + if (state == DFU_THREAD_STATE_IDLE) { + state = DFU_THREAD_STATE_FLASHING; + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + } + + frame = TAILQ_FIRST(&ctx->ioq); + + TAILQ_REMOVE(&ctx->ioq, frame, entry); + + pthread_mutex_unlock(&ctx->mutex); + + ret = write(e->fd, frame->buf, frame->len); + + if (ret < frame->len) { + fprintf(stderr, "Error occurs while flashing\n"); + state = DFU_THREAD_STATE_ERROR; + } + + progress += frame->len; + + fprintf(stdout, "#"); + fflush(stdout); + + dfu_put_buffer(frame->buf); + free(frame); + + /* transfer finished */ + if (state != DFU_THREAD_STATE_ERROR + && progress >= e->file_size) { + e->transfer_done = 1; + + state = DFU_THREAD_STATE_IDLE; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + + pthread_cond_signal(&ctx->write_done); + + pthread_mutex_lock(&ctx->sync_mutex); + pthread_cond_wait(&ctx->sync_done, + &ctx->sync_mutex); + pthread_mutex_unlock(&ctx->sync_mutex); + break; + } + } + + pthread_cleanup_pop(1); + + return NULL; } -static int dfu_start_entity(struct tfm_context *ctx, int idx, unsigned long size) +static int dfu_start_entry(struct dfu_entry *e, char **entry, uint64_t size) { - char **info = dfu_info[idx]; char *file; - int fd; + int fd, ret = 0; - switch (*info[DFU_INFO_MODE]) { + switch (*entry[DFU_INFO_MODE]) { case DFU_INFO_MODE_PARTITION: - file = strdup(info[DFU_INFO_PARTITION]); + file = strdup(entry[DFU_INFO_PARTITION]); if (!file) return -ENOMEM; break; case DFU_INFO_MODE_FILE: { - int ret; int path_prefix = strlen(DFU_MOUNT_PATH); - int path_suffix = strlen(info[DFU_INFO_PATH]); - int path_name = strlen(info[DFU_INFO_NAME]); + int path_suffix = strlen(entry[DFU_INFO_PATH]); + int path_name = strlen(entry[DFU_INFO_NAME]); - ret = mount_dev(info[DFU_INFO_PARTITION]); + ret = mount_dev(entry[DFU_INFO_PARTITION]); if (ret < 0) return -EINVAL; @@ -236,138 +333,98 @@ static int dfu_start_entity(struct tfm_context *ctx, int idx, unsigned long size } strncpy(file, DFU_MOUNT_PATH, path_prefix + 1); - strncat(file, info[DFU_INFO_PATH], path_suffix + 1); - strncat(file, info[DFU_INFO_NAME], path_name + 1); + strncat(file, entry[DFU_INFO_PATH], path_suffix + 1); + strncat(file, entry[DFU_INFO_NAME], path_name + 1); break; } default: - fprintf(stderr, "flash entry '%s' has wrong mode\n", info[DFU_INFO_NAME]); + fprintf(stderr, + "flash entry '%s' has wrong mode\n", + entry[DFU_INFO_NAME]); return -EINVAL; } fd = open(file, O_WRONLY); if (fd < 0) { - fprintf(stderr, "cannot open target: %s\n", info[DFU_INFO_NAME]); - free(file); - return -EIO; + fprintf(stderr, + "cannot open target: %s\n", + entry[DFU_INFO_NAME]); + ret = -EIO; + goto err; } - ctx->dfu_fd = fd; - - ctx->dfu_info = info; - ctx->transfer_done = 0; + e->entry = entry; + e->fd = fd; + e->file_size = size; + e->transfer_done = 0; +err: free(file); - return 0; + return ret; } -int dfu_start(struct tfm_context *ctx, const char *entity) +struct dfu_entry *dfu_start(struct dfu_context *ctx, + uint64_t size, const char *filename) { - 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; - } + struct dfu_entry *e; + char **entry; + int ret; - ret = dfu_start_entity(ctx, idx, size); - if (ret < 0) { - fprintf(stderr, "Cannot start download: %s\n", entity); - return -EINVAL; + entry = find_entry_match(ctx, filename); + if (!entry) { + fprintf(stderr, "Cannot find dfu info for %s\n", filename); + return NULL; } - fprintf(stdout, "Start download: %s...", entity); - fflush(stdout); + e = (struct dfu_entry *)malloc(sizeof(*e)); + if (!e) + return NULL; - return 0; -} + ret = dfu_start_entry(e, entry, size); + if (ret < 0) { + fprintf(stderr, "Cannot start download: %s\n", filename); + free(e); + return NULL; + } -static void dfu_thread_cleanup(void *ptr) -{ - struct tfm_context *ctx = ptr; - struct dfu_frame *frame; + pthread_create(&ctx->thread, NULL, dfu_thread_main, e); - while (!TAILQ_EMPTY(&ctx->dfu_ioq_head)) { - frame = TAILQ_FIRST(&ctx->dfu_ioq_head); + e->ctx = ctx; - TAILQ_REMOVE(&ctx->dfu_ioq_head, frame, entry); + fprintf(stdout, "Start download: %s...", filename); + fflush(stdout); - dfu_put_buffer(frame->buf); - free(frame); - } + return e; } -static void *dfu_thread_main(void *ptr) +void dfu_sync(struct dfu_entry *e) { - struct tfm_context *ctx = ptr; - struct dfu_frame *frame; - int state = DFU_THREAD_STATE_IDLE; - uint64_t progress = 0; - int ret; - - pthread_cleanup_push(dfu_thread_cleanup, ptr); - - while (state != DFU_THREAD_STATE_ERROR) { - pthread_mutex_lock(&ctx->dfu_mutex); - - while (TAILQ_EMPTY(&ctx->dfu_ioq_head)) { - pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); - state = DFU_THREAD_STATE_IDLE; - pthread_cond_wait(&ctx->dfu_data_arrive, &ctx->dfu_mutex); - } - - if (state == DFU_THREAD_STATE_IDLE) { - state = DFU_THREAD_STATE_FLASHING; - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); - } - - frame = TAILQ_FIRST(&ctx->dfu_ioq_head); - - TAILQ_REMOVE(&ctx->dfu_ioq_head, frame, entry); - - pthread_mutex_unlock(&ctx->dfu_mutex); + struct dfu_context *ctx = e->ctx; + char **info = e->entry; - ret = write(ctx->dfu_fd, frame->buf, frame->len); - - if (ret < frame->len) { - fprintf(stderr, "Error occurs while flashing\n"); - state = DFU_THREAD_STATE_ERROR; - } + pthread_mutex_lock(&ctx->sync_mutex); + if (!e->transfer_done) + pthread_cond_wait(&ctx->write_done, &ctx->sync_mutex); + pthread_mutex_unlock(&ctx->sync_mutex); - progress += frame->len; - - fprintf(stdout, "#"); - fflush(stdout); - - dfu_put_buffer(frame->buf); - free(frame); - - /* transfer finished */ - if (state != DFU_THREAD_STATE_ERROR && progress >= ctx->thor_file_size) { - progress = 0; - ctx->transfer_done = 1; - - fprintf(stdout, "\nTransfer completed. Please wait for sync..."); - fflush(stdout); - - state = DFU_THREAD_STATE_IDLE; - pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); - - 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); - } + switch (*info[DFU_INFO_MODE]) { + case DFU_INFO_MODE_FILE: + fsync(e->fd); + close(e->fd); + umount_dev(); + break; + case DFU_INFO_MODE_PARTITION: + close(e->fd); + break; + default: + break; } - pthread_cleanup_pop(1); + pthread_cond_signal(&ctx->sync_done); - return NULL; + fprintf(stdout, "finished\n"); } static int parse_dfu_info(char *buf, char **info) @@ -388,36 +445,35 @@ static int parse_dfu_info(char *buf, char **info) return 0; } -static void destroy_dfu_info(void) +static void destroy_dfu_info(struct dfu_context *ctx) { int i, j; - for (i = 0; i < DFU_INFO_NUM; i++) { + for (i = 0; i < DFU_ENTRY_LIST_MAXLEN; i++) { for (j = 0; j < DFU_INFO_MAX; j++) - if (dfu_info[i][j]) { - free(dfu_info[i][j]); - dfu_info[i][j] = NULL; + if (ctx->dfu_entry_list[i][j]) { + free(ctx->dfu_entry_list[i][j]); + ctx->dfu_entry_list[i][j] = NULL; } } } - -static int init_dfu_info(const char *dfu_info_file) +int dfu_init_entry(struct dfu_context *ctx, const char *entry_file) { FILE *fp; char buf[1024]; int i = 0; int ret; - fp = fopen(dfu_info_file, "r"); + fp = fopen(entry_file, "r"); if (!fp) return -ENOENT; - while (i < DFU_INFO_NUM && !feof(fp)) { + while (i < DFU_ENTRY_LIST_MAXLEN && !feof(fp)) { if (fgets(buf, 1024, fp) == NULL) break; - ret = parse_dfu_info(buf, dfu_info[i++]); + ret = parse_dfu_info(buf, ctx->dfu_entry_list[i++]); if (ret < 0) { fprintf(stderr, "cannot parse dfu info"); goto err_free_all; @@ -430,45 +486,39 @@ static int init_dfu_info(const char *dfu_info_file) err_free_all: fclose(fp); - destroy_dfu_info(); + destroy_dfu_info(ctx); return ret; } -int dfu_init(struct tfm_context *ctx, const char *dfu_info_file) +struct dfu_context *dfu_alloc_context(void) { - int ret; + struct dfu_context *ctx; - ret = init_dfu_info(dfu_info_file); - if (ret < 0) { - fprintf(stderr, "failed to get flash entries\n"); - return ret; - } + ctx = (struct dfu_context *)malloc(sizeof(*ctx)); + if (!ctx) + return NULL; - TAILQ_INIT(&ctx->dfu_ioq_head); + ctx->thread = 0; + TAILQ_INIT(&ctx->ioq); - 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); + pthread_mutex_init(&ctx->mutex, NULL); + pthread_mutex_init(&ctx->sync_mutex, NULL); + pthread_cond_init(&ctx->data_arrived, NULL); + pthread_cond_init(&ctx->write_done, NULL); + pthread_cond_init(&ctx->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; - } + memset(ctx->dfu_entry_list, 0, sizeof(ctx->dfu_entry_list)); - return 0; + return ctx; } -void dfu_exit(struct tfm_context *ctx) +void dfu_free_context(struct dfu_context *ctx) { - pthread_cancel(ctx->dfu_thread); - pthread_join(ctx->dfu_thread, NULL); - destroy_dfu_info(); - if (ctx->connect) { - free(ctx->connect); - ctx->connect = NULL; - } + if (!ctx) + return; + + pthread_cancel(ctx->thread); + destroy_dfu_info(ctx); + free(ctx); } diff --git a/src/dfu.h b/src/dfu.h index 589a9a1..0d35883 100644 --- a/src/dfu.h +++ b/src/dfu.h @@ -18,41 +18,27 @@ #define __DFU_H #include -#include "tfm.h" +#include -#define DFU_DELIMITER " :,\t\n" -#define DFU_INFO_NUM 100 -#define DFU_MOUNT_PATH "/mnt/tfm-temp" +struct dfu_context; -#define DFU_INFO_MODE_PARTITION 'p' -#define DFU_INFO_MODE_FILE 'f' - -enum dfu_info_entry { - DFU_INFO_MODE = 0, - DFU_INFO_PARTITION, - DFU_INFO_NAME, - DFU_INFO_PATH, - DFU_INFO_MAX, -}; - -enum dfu_thread_state { - DFU_THREAD_STATE_ERROR = -1, - DFU_THREAD_STATE_IDLE, - DFU_THREAD_STATE_FLASHING, -}; - -struct dfu_frame { - void *buf; - unsigned long len; - TAILQ_ENTRY(dfu_frame) entry; +struct dfu_entry { + struct dfu_context *ctx; + char **entry; + int fd; + uint64_t file_size; + int transfer_done; }; 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); +struct dfu_context *dfu_alloc_context(void); +void dfu_free_context(struct dfu_context *ctx); +int dfu_init_entry(struct dfu_context *ctx, const char *entry_file); +struct dfu_entry *dfu_start(struct dfu_context *ctx, + uint64_t size, const char *filename); +void dfu_sync(struct dfu_entry *e); +int dfu_request_io(struct dfu_entry *e, void *buffer, unsigned long len); + #endif diff --git a/src/main.c b/src/main.c index 32c2473..376d8be 100644 --- a/src/main.c +++ b/src/main.c @@ -25,12 +25,13 @@ int _main(int argc, char *argv[]) { - struct tfm_context ctx; + struct tfm_context tfm; + struct dfu_context *dfu; const char *part_table = "/usr/share/partition.info"; char *opt_table = NULL; int ret, opt; - memset(&ctx, 0, sizeof(ctx)); + memset(&tfm, 0, sizeof(tfm)); while ((opt = getopt(argc, argv, "p:i:")) != -1) { switch (opt) { @@ -65,7 +66,7 @@ int _main(int argc, char *argv[]) ret = -1; goto out_optfree; } - ctx.port = (int)val; + tfm.port = (int)val; break; } default: @@ -74,35 +75,41 @@ int _main(int argc, char *argv[]) } } - ret = dfu_init(&ctx, part_table); - if (ret < 0) { + dfu = dfu_alloc_context(); + if (!dfu) { ret = -1; goto out_optfree; } - ret = net_connect(&ctx); + ret = dfu_init_entry(dfu, part_table); + if (ret < 0) { + ret = -1; + goto out_dfuexit; + } + + ret = net_connect(&tfm); if (ret < 0) { ret = -1; goto out_dfuexit; } - ret = thor_init(&ctx); + ret = thor_setup(&tfm); if (ret < 0) { ret = -1; goto out_netdisconn; } - ret = thor_process(&ctx); + ret = thor_process(&tfm, dfu); if (ret < 0) { ret = -1; goto out_netdisconn; } out_netdisconn: - net_disconnect(&ctx); + net_disconnect(&tfm); out_dfuexit: - dfu_exit(&ctx); + dfu_free_context(dfu); out_optfree: if (opt_table) diff --git a/src/tfm.h b/src/tfm.h index 747de2d..8760e35 100644 --- a/src/tfm.h +++ b/src/tfm.h @@ -19,9 +19,10 @@ #include #include -#include #include +#include "dfu.h" + struct tfm_connect { int fd; ssize_t (*rx_data)(int fd, void *buf, ssize_t len); @@ -29,26 +30,11 @@ struct tfm_connect { }; 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); +int thor_setup(struct tfm_context *tfm); +int thor_process(struct tfm_context *tfm, struct dfu_context *dfu); #endif diff --git a/src/thor.c b/src/thor.c index e19d33f..1c255d2 100644 --- a/src/thor.c +++ b/src/thor.c @@ -26,18 +26,20 @@ #include #include +#include "tfm.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); -} +struct thor_context { + struct rqt_pkt *rqt; + struct tfm_connect *intf; + struct dfu_context *dfu; + struct dfu_entry *dfu_entry; -static inline ssize_t thor_tx_data(struct tfm_connect *conn, void *buf, ssize_t len) -{ - return conn->tx_data(conn->fd, buf, len); -} + void *buffer; + uint64_t file_size; + uint64_t remain; +}; static unsigned int _checkboard(void) { @@ -48,19 +50,21 @@ static unsigned int _checkboard(void) return -1; } -static int thor_send_rsp(struct tfm_connect *conn, const struct res_pkt *rsp) +static int thor_send_rsp(struct thor_context *tctx, struct res_pkt *rsp) { + struct tfm_connect *intf = tctx->intf; ssize_t n; - n = thor_tx_data(conn, (void *)rsp, RES_PKT_SIZE); + n = intf->tx_data(intf->fd, (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) +static int thor_process_rqt_info(struct thor_context *tctx) { + struct rqt_pkt *rqt = tctx->rqt; struct res_pkt rsp; int ret; @@ -89,7 +93,7 @@ static int thor_process_rqt_info(struct tfm_context *ctx, struct rqt_pkt *rqt) return -EINVAL; } - ret = thor_send_rsp(ctx->connect, &rsp); + ret = thor_send_rsp(tctx, &rsp); if (ret < 0) { fprintf(stderr, "failed to send response of REQUEST_INFO\n"); return ret; @@ -98,9 +102,9 @@ static int thor_process_rqt_info(struct tfm_context *ctx, struct rqt_pkt *rqt) return 0; } -static int thor_process_rqt_cmd(struct tfm_context *ctx, struct rqt_pkt *rqt) +static int thor_process_rqt_cmd(struct thor_context *tctx) { - struct tfm_connect *conn = ctx->connect; + struct rqt_pkt *rqt = tctx->rqt; struct res_pkt rsp; int ret; @@ -111,7 +115,7 @@ static int thor_process_rqt_cmd(struct tfm_context *ctx, struct rqt_pkt *rqt) switch (rqt->sub_id) { case RQT_CMD_REBOOT: - thor_send_rsp(conn, &rsp); + thor_send_rsp(tctx, &rsp); ret = reboot(RB_AUTOBOOT); if (ret < 0) { @@ -120,7 +124,7 @@ static int thor_process_rqt_cmd(struct tfm_context *ctx, struct rqt_pkt *rqt) } break; case RQT_CMD_POWEROFF: - thor_send_rsp(conn, &rsp); + thor_send_rsp(tctx, &rsp); break; default: fprintf(stderr, "Not supported command request: %d", @@ -131,29 +135,32 @@ static int thor_process_rqt_cmd(struct tfm_context *ctx, struct rqt_pkt *rqt) return 0; } -static void thor_send_data_rsp(struct tfm_connect *conn, int ack, int count) +static void thor_send_data_rsp(struct thor_context *tctx, int ack, int count) { + struct tfm_connect *intf = tctx->intf; struct data_res_pkt rsp; rsp.ack = ack; rsp.cnt = count; - thor_tx_data(conn, &rsp, DATA_RES_PKT_SIZE); + intf->tx_data(intf->fd, &rsp, DATA_RES_PKT_SIZE); } -static int thor_download_head(struct tfm_context *ctx, unsigned int packet_size) +static int +thor_download_head(struct thor_context *tctx, unsigned int packet_size) { - struct tfm_connect *conn = ctx->connect; + struct tfm_connect *intf = tctx->intf; + struct dfu_entry *e = tctx->dfu_entry; uint64_t recv = 0; - uint64_t total = ctx->thor_file_size; + uint64_t total = tctx->file_size; void *buf; int usb_pkt_cnt = 0, n; int ret; - ctx->transfer_buffer = buf = dfu_get_buffer(FLASH_UNIT_SIZE); + tctx->buffer = buf = dfu_get_buffer(FLASH_UNIT_SIZE); while (total - recv >= packet_size) { - n = thor_rx_data(conn, buf, packet_size); + n = intf->rx_data(intf->fd, buf, packet_size); if (n < packet_size) return -EIO; @@ -162,52 +169,52 @@ static int thor_download_head(struct tfm_context *ctx, unsigned int packet_size) buf += packet_size; if ((recv % FLASH_UNIT_SIZE) == 0) { - ret = dfu_request_io(ctx, FLASH_UNIT_SIZE); + ret = dfu_request_io(e, tctx->buffer, FLASH_UNIT_SIZE); if (ret < 0) return -EIO; - ctx->transfer_buffer = buf = dfu_get_buffer(FLASH_UNIT_SIZE); + tctx->buffer = buf = dfu_get_buffer(FLASH_UNIT_SIZE); } - thor_send_data_rsp(conn, 0, ++usb_pkt_cnt); + thor_send_data_rsp(tctx, 0, ++usb_pkt_cnt); } - ctx->remain = (total - recv) + (uint64_t)(buf - ctx->transfer_buffer); + tctx->remain = (total - recv) + (uint64_t)(buf - tctx->buffer); if ((total - recv) > 0) { - n = thor_rx_data(conn, buf, packet_size); + n = intf->rx_data(intf->fd, buf, packet_size); if (n < packet_size) return -EIO; recv += n; - thor_send_data_rsp(conn, 0, ++usb_pkt_cnt); + thor_send_data_rsp(tctx, 0, ++usb_pkt_cnt); } return 0; } -static int thor_download_tail(struct tfm_context *ctx) +static int thor_download_tail(struct thor_context *tctx) { + struct dfu_entry *e = tctx->dfu_entry; int ret; - if (ctx->remain) { - ret = dfu_request_io(ctx, ctx->remain); + if (tctx->remain) { + ret = dfu_request_io(e, tctx->buffer, tctx->remain); if (ret < 0) return -EIO; } else { /* if there is no remain, buffer should be freed */ - dfu_put_buffer(ctx->transfer_buffer); + dfu_put_buffer(tctx->buffer); } - dfu_sync(ctx); + dfu_sync(e); return 0; } -static int thor_process_rqt_download(struct tfm_context *ctx, struct rqt_pkt *rqt) +static int thor_process_rqt_download(struct thor_context *tctx) { - struct tfm_connect *conn = ctx->connect; - char f_name[FILE_NAME_MAXLEN + 1] = {0,}; + struct rqt_pkt *rqt = tctx->rqt; struct res_pkt rsp; - int ret = 0, head, file_type; + int ret = 0; memset(&rsp, 0, RES_PKT_SIZE); @@ -216,10 +223,16 @@ static int thor_process_rqt_download(struct tfm_context *ctx, struct rqt_pkt *rq switch (rqt->sub_id) { case RQT_DL_INIT: - ctx->thor_file_size = (uint64_t)rqt->int_data[0] + tctx->file_size = (uint64_t)rqt->int_data[0] + ((uint64_t)rqt->int_data[1] << 32); break; case RQT_DL_FILE_INFO: + { + struct dfu_context *dfu = tctx->dfu; + struct dfu_entry *e; + char f_name[FILE_NAME_MAXLEN + 1] = {0,}; + int file_type; + file_type = rqt->int_data[0]; if (file_type != BINARY_TYPE_NORMAL) { fprintf(stderr, "Currently only NORMAL_FILE is supported\n"); @@ -227,34 +240,45 @@ static int thor_process_rqt_download(struct tfm_context *ctx, struct rqt_pkt *rq break; } - ctx->thor_file_size = (uint64_t)rqt->int_data[1] + tctx->file_size = (uint64_t)rqt->int_data[1] + ((uint64_t)rqt->int_data[2] << 32); + 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) { + e = dfu_start(dfu, tctx->file_size, f_name); + if (!e) { fprintf(stderr, "failed to start dfu\n"); ret = -EINVAL; } + + tctx->dfu_entry = e; break; + } case RQT_DL_FILE_START: - ret = thor_send_rsp(conn, &rsp); + ret = thor_send_rsp(tctx, &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; + ret = thor_download_head(tctx, DATA_PKT_SIZE); + if (ret < 0) + tctx->remain = 0; - return head; + return ret; case RQT_DL_FILE_END: - rsp.ack = thor_download_tail(ctx); + rsp.ack = thor_download_tail(tctx); ret = rsp.ack; - ctx->remain = 0; + tctx->remain = 0; + tctx->file_size = 0; + /* + * dfu_entry and buffer will be freed by dfu_thread_cleanup, + * so just make it NULL here. + */ + tctx->dfu_entry = NULL; + tctx->buffer = NULL; break; case RQT_DL_EXIT: break; @@ -264,23 +288,24 @@ static int thor_process_rqt_download(struct tfm_context *ctx, struct rqt_pkt *rq ret = -EINVAL; } - thor_send_rsp(conn, &rsp); + thor_send_rsp(tctx, &rsp); return ret; } -static int thor_do_request(struct tfm_context *ctx, struct rqt_pkt *rqt) +static int thor_do_request(struct thor_context *tctx) { + struct rqt_pkt *rqt = tctx->rqt; int ret; switch (rqt->id) { case RQT_INFO: - ret = thor_process_rqt_info(ctx, rqt); + ret = thor_process_rqt_info(tctx); break; case RQT_CMD: - ret = thor_process_rqt_cmd(ctx, rqt); + ret = thor_process_rqt_cmd(tctx); break; case RQT_DL: - ret = thor_process_rqt_download(ctx, rqt); + ret = thor_process_rqt_download(tctx); break; case RQT_UL: fprintf(stderr, "Request \"UPLOAD\" is not supported\n"); @@ -294,17 +319,17 @@ static int thor_do_request(struct tfm_context *ctx, struct rqt_pkt *rqt) return ret; } -static int thor_handshake(struct tfm_connect *conn) +static int thor_handshake(struct tfm_connect *intf) { char buf[5]; ssize_t n; - n = thor_rx_data(conn, buf, 4/* strlen("THOR") */); + n = intf->rx_data(intf->fd, buf, 4/* strlen("THOR") */); if (n < 4) return -EIO; if (!strncmp(buf, "THOR", 4)) { - n = thor_tx_data(conn, "ROHT", 4); + n = intf->tx_data(intf->fd, "ROHT", 4); if (n < 4) return -EIO; } else { @@ -315,20 +340,21 @@ static int thor_handshake(struct tfm_connect *conn) return 0; } -int thor_init(struct tfm_context *ctx) +int thor_process(struct tfm_context *tfm, struct dfu_context *dfu) { - return thor_handshake(ctx->connect); -} - -int thor_process(struct tfm_context *ctx) -{ - struct tfm_connect *conn = ctx->connect; + struct tfm_connect *intf = tfm->connect; + struct thor_context tctx; struct rqt_pkt rqt; ssize_t n; int ret; + memset(&tctx, 0, sizeof(tctx)); + + tctx.intf = intf; + tctx.dfu = dfu; + for (;;) { - n = thor_rx_data(conn, &rqt, RQT_PKT_SIZE); + n = intf->rx_data(intf->fd, &rqt, RQT_PKT_SIZE); if (n < sizeof(rqt)) { fprintf(stderr, "Failed to receive data from the host(%zd:%zu)", @@ -336,10 +362,22 @@ int thor_process(struct tfm_context *ctx) return -EIO; } - ret = thor_do_request(ctx, &rqt); + tctx.rqt = &rqt; + + ret = thor_do_request(&tctx); if (ret < 0) return ret; } return 0; } + +int thor_setup(struct tfm_context *tfm) +{ + if (!tfm->connect) { + fprintf(stderr, "Invalid connection\n"); + return -EINVAL; + } + + return thor_handshake(tfm->connect); +} -- 2.7.4 From 5df0e1d2947d87e15492f3fe103dcd4d68992beb Mon Sep 17 00:00:00 2001 From: Dongwoo Lee Date: Tue, 17 Mar 2020 13:36:02 +0900 Subject: [PATCH 12/16] interface: Add support for multiple transfer interfaces To support USB connection, multiple interface f/w should be required before. This adds only interface f/w, so network connection is still processed without f/w. Change-Id: I367f9e961daefe3b9005104084955edbe0851057 Signed-off-by: Dongwoo Lee --- CMakeLists.txt | 2 +- src/interface.c | 222 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/interface.h | 70 ++++++++++++++++++ 3 files changed, 293 insertions(+), 1 deletion(-) create mode 100644 src/interface.c create mode 100644 src/interface.h diff --git a/CMakeLists.txt b/CMakeLists.txt index c722f06..9fcc572 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ SET(CMAKE_EXE_LINKER_FLAGS "-pie") FIND_PACKAGE(Threads REQUIRED) -ADD_EXECUTABLE(${PROJECT_NAME} src/main.c src/thor.c src/dfu.c src/net.c) +ADD_EXECUTABLE(${PROJECT_NAME} src/main.c src/thor.c src/dfu.c src/interface.c src/net.c) INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/src) diff --git a/src/interface.c b/src/interface.c new file mode 100644 index 0000000..de6f559 --- /dev/null +++ b/src/interface.c @@ -0,0 +1,222 @@ +/* + * 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 "tfm.h" +#include "interface.h" + +struct tfm_interface_context { + struct tfm_interface *connected; + + pthread_mutex_t mutex; + pthread_cond_t signal; + + LIST_HEAD(intflist, tfm_interface) interface_list; +}; + +INTERFACE interface_avail_list = LIST_HEAD_INITIALIZER(interface_avail_list); + +ssize_t tfm_interface_send(struct tfm_interface *intf, void *buf, ssize_t len) +{ + if (!intf) + return -EINVAL; + + if (intf->ops && intf->ops->tx_data) + return intf->ops->tx_data(intf->txd, buf, len); + + return -ENOENT; +} + +ssize_t tfm_interface_recv(struct tfm_interface *intf, void *buf, ssize_t len) +{ + if (!intf) + return -EINVAL; + + if (intf->ops && intf->ops->rx_data) + return intf->ops->rx_data(intf->rxd, buf, len); + + return -ENOENT; +} + +int tfm_interface_set_private(struct tfm_interface_context *ictx, + char *name, void *data) +{ + struct tfm_interface *intf = NULL; + + LIST_FOREACH(intf, &ictx->interface_list, entry) { + if (!strncmp(intf->name, name, strlen(name))) + break; + } + + if (!intf) { + fprintf(stderr, "cannot find interface: %s\n", name); + return -ENOENT; + } + + intf->priv = data; + + return 0; +} + +static void *connect_thread_main(void *ptr) +{ + struct tfm_interface *intf = ptr; + struct tfm_interface_context *ictx = intf->ctx; + int ret; + + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + + /* for checking which interface is connected */ + intf->tid = pthread_self(); + + if (!intf->ops || !intf->ops->connect || !intf->ops->disconnect) + return NULL; + + ret = intf->ops->connect(intf); + if (ret < 0) { + fprintf(stderr, "failed to connect: %s\n", intf->name); + return NULL; + } + + pthread_mutex_lock(&ictx->mutex); + if (ictx->connected) { + pthread_mutex_unlock(&ictx->mutex); + fprintf(stderr, + "interface %s will be ignored due to %s\n", + intf->name, ictx->connected->name); + intf->ops->disconnect(intf); + return NULL; + } + + ictx->connected = intf; + pthread_mutex_unlock(&ictx->mutex); + + fprintf(stdout, "interface %s is connected.\n", intf->name); + + pthread_cond_signal(&ictx->signal); + + return NULL; +} + +struct tfm_interface *tfm_interface_connect(struct tfm_interface_context *ictx) +{ + struct tfm_interface *intf; + + LIST_FOREACH(intf, &ictx->interface_list, entry) { + pthread_t t; + + pthread_create(&t, NULL, connect_thread_main, (void *)intf); + pthread_detach(t); + } + + pthread_mutex_lock(&ictx->mutex); + while (!ictx->connected) + pthread_cond_wait(&ictx->signal, &ictx->mutex); + pthread_mutex_unlock(&ictx->mutex); + + LIST_FOREACH(intf, &ictx->interface_list, entry) { + /* Terminate threads waiting for other connection */ + if (intf != ictx->connected) { + intf->ctx = NULL; + pthread_cancel(intf->tid); + } + } + + return ictx->connected; +} + +void tfm_interface_disconnect(struct tfm_interface *intf) +{ + if (!intf) + return; + + if (intf->ops && intf->ops->disconnect) + intf->ops->disconnect(intf); + + intf->ctx->connected = NULL; +} + +static void destroy_interface_list(struct tfm_interface_context *ictx) +{ + struct tfm_interface *intf, *temp; + + intf = LIST_FIRST(&ictx->interface_list); + while (intf != NULL) { + temp = intf; + intf = LIST_NEXT(intf, entry); + + LIST_REMOVE(temp, entry); + free(temp); + } +} + +struct tfm_interface_context *tfm_interface_init(void) +{ + struct tfm_interface_context *ictx; + struct tfm_interface_driver *driver; + + ictx = (struct tfm_interface_context *)malloc(sizeof(*ictx)); + if (!ictx) + return NULL; + + ictx->connected = NULL; + + pthread_mutex_init(&ictx->mutex, NULL); + pthread_cond_init(&ictx->signal, NULL); + + LIST_INIT(&ictx->interface_list); + + LIST_FOREACH(driver, &interface_avail_list, entry) { + struct tfm_interface *intf; + + intf = (struct tfm_interface *)malloc(sizeof(*intf)); + if (!intf) + goto err; + + intf->name = driver->name; + intf->rxd = intf->txd = -1; + intf->ops = &driver->ops; + intf->priv = NULL; + intf->tid = 0; + intf->ctx = ictx; + + LIST_INSERT_HEAD(&ictx->interface_list, intf, entry); + } + + return ictx; + +err: + destroy_interface_list(ictx); + free(ictx); + + return NULL; +} + +void tfm_interface_exit(struct tfm_interface_context *ictx) +{ + if (!ictx) + return; + + tfm_interface_disconnect(ictx->connected); + destroy_interface_list(ictx); + free(ictx); +} diff --git a/src/interface.h b/src/interface.h new file mode 100644 index 0000000..7bb9c02 --- /dev/null +++ b/src/interface.h @@ -0,0 +1,70 @@ +/* + * 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 __INTERFACE_H +#define __INTERFACE_H + +#include +#include +#include + +typedef LIST_HEAD(availlist, tfm_interface_driver) INTERFACE; + +#define INTERFACE_REGISTER(_intf) extern INTERFACE interface_avail_list; \ +static void __attribute__ ((constructor)) tfm_##_intf##_interface_init(void) \ +{ \ + LIST_INSERT_HEAD(&interface_avail_list, &_intf, entry); \ +} + +struct tfm_interface_context; +struct tfm_interface; + +struct tfm_interface_ops { + int (*connect)(struct tfm_interface *intf); + int (*disconnect)(struct tfm_interface *intf); + ssize_t (*rx_data)(int fd, void *buf, ssize_t len); + ssize_t (*tx_data)(int fd, void *buf, ssize_t len); +}; + +struct tfm_interface_driver { + char *name; + struct tfm_interface_ops ops; + + LIST_ENTRY(tfm_interface_driver) entry; +}; + +struct tfm_interface { + char *name; + int rxd; + int txd; + struct tfm_interface_ops *ops; + + void *priv; + pthread_t tid; + struct tfm_interface_context *ctx; + + LIST_ENTRY(tfm_interface) entry; +}; + +ssize_t tfm_interface_send(struct tfm_interface *intf, void *buf, ssize_t len); +ssize_t tfm_interface_recv(struct tfm_interface *intf, void *buf, ssize_t len); +int tfm_interface_set_private(struct tfm_interface_context *ictx, + char *name, void *data); +struct tfm_interface *tfm_interface_connect(struct tfm_interface_context *ictx); +void tfm_interface_disconnect(struct tfm_interface *intf); +struct tfm_interface_context *tfm_interface_init(void); +void tfm_interface_exit(struct tfm_interface_context *ictx); +#endif -- 2.7.4 From bfa980fadb2273fb752ea4b45f4177cd012065e2 Mon Sep 17 00:00:00 2001 From: Dongwoo Lee Date: Tue, 17 Mar 2020 13:42:38 +0900 Subject: [PATCH 13/16] interface: net: Apply interface f/w into network transfer Since this, network connection is processed upon interface f/w. Change-Id: I87df71a923f010bb013e0b4166c50949bb19a153 Signed-off-by: Dongwoo Lee --- src/main.c | 37 +++++++++++++++++++------------ src/net.c | 74 +++++++++++++++++++++++++++++++++----------------------------- src/net.h | 27 ----------------------- src/tfm.h | 18 +++------------ src/thor.c | 34 ++++++++++++++--------------- 5 files changed, 83 insertions(+), 107 deletions(-) delete mode 100644 src/net.h diff --git a/src/main.c b/src/main.c index 376d8be..141f165 100644 --- a/src/main.c +++ b/src/main.c @@ -20,18 +20,24 @@ #include #include +#include "tfm.h" #include "dfu.h" -#include "net.h" +#include "interface.h" int _main(int argc, char *argv[]) { - struct tfm_context tfm; + struct tfm_interface_context *supported_interfaces; struct dfu_context *dfu; + struct tfm_interface *intf; const char *part_table = "/usr/share/partition.info"; char *opt_table = NULL; int ret, opt; - memset(&tfm, 0, sizeof(tfm)); + supported_interfaces = tfm_interface_init(); + if (!supported_interfaces) { + ret = -1; + goto out; + } while ((opt = getopt(argc, argv, "p:i:")) != -1) { switch (opt) { @@ -45,13 +51,13 @@ int _main(int argc, char *argv[]) } else { fprintf(stderr, "Out of memory\n"); ret = -1; - goto out; + goto out_intfexit; } } else { fprintf(stderr, "path should be specified with '-i'\n"); ret = -1; - goto out; + goto out_intfexit; } break; case 'p': @@ -66,7 +72,8 @@ int _main(int argc, char *argv[]) ret = -1; goto out_optfree; } - tfm.port = (int)val; + tfm_interface_set_private(supported_interfaces, + "net", (void *)val); break; } default: @@ -87,26 +94,26 @@ int _main(int argc, char *argv[]) goto out_dfuexit; } - ret = net_connect(&tfm); - if (ret < 0) { + intf = tfm_interface_connect(supported_interfaces); + if (!intf) { ret = -1; goto out_dfuexit; } - ret = thor_setup(&tfm); + ret = thor_setup(intf); if (ret < 0) { ret = -1; - goto out_netdisconn; + goto out_disconn; } - ret = thor_process(&tfm, dfu); + ret = thor_process(intf, dfu); if (ret < 0) { ret = -1; - goto out_netdisconn; + goto out_disconn; } -out_netdisconn: - net_disconnect(&tfm); +out_disconn: + tfm_interface_disconnect(intf); out_dfuexit: dfu_free_context(dfu); @@ -114,6 +121,8 @@ out_dfuexit: out_optfree: if (opt_table) free(opt_table); +out_intfexit: + tfm_interface_exit(supported_interfaces); out: exit(ret); diff --git a/src/net.c b/src/net.c index b946fa4..9de6741 100644 --- a/src/net.c +++ b/src/net.c @@ -23,80 +23,86 @@ #include #include -#include "net.h" +#include "tfm.h" +#include "interface.h" -static ssize_t net_rx_data(int sock, void *buf, ssize_t len) +#define DEFAULT_PORT 23456 + +static ssize_t net_rx_data(int fd, void *buf, ssize_t len) { - return recv(sock, buf, len, MSG_WAITALL); + return recv(fd, buf, len, MSG_WAITALL); } -static ssize_t net_tx_data(int sock, void *buf, ssize_t len) +static ssize_t net_tx_data(int fd, void *buf, ssize_t len) { - return send(sock, buf, len, 0); + return send(fd, buf, len, 0); } -int net_connect(struct tfm_context *ctx) +static int net_connect(struct tfm_interface *intf) { struct sockaddr_in servaddr; - struct tfm_connect *conn; - int listener, sock; - int ret = 0; - - conn = malloc(sizeof(*conn)); - if (!conn) - return -ENOMEM; + int listener, ret = 0; + int port; listener = socket(AF_INET, SOCK_STREAM, 0); if (listener < 0) { fprintf(stderr, "Failed to create socket\n"); - ret = -EINVAL; - goto err_free; + return -EINVAL; } + port = (unsigned long) intf->priv; + if (port <= 1024) + port = DEFAULT_PORT; + + fprintf(stdout, "%s using port: %d\n", intf->name, port); + 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)); + servaddr.sin_port = htons(port); ret = bind(listener, (struct sockaddr *) &servaddr, sizeof(servaddr)); if (ret < 0) { fprintf(stderr, "Failed to bind socket\n"); - goto err_socket; + goto err; } ret = listen(listener, 1024); if (ret < 0) { fprintf(stderr, "Failed to call listen\n"); - goto err_socket; + goto err; } - sock = accept(listener, NULL, NULL); - if (sock < 0) { + ret = accept(listener, NULL, NULL); + if (ret < 0) { fprintf(stderr, "Failed to accept connection\n"); - ret = sock; - goto err_socket; + goto err; } - conn->fd = sock; - conn->rx_data = net_rx_data; - conn->tx_data = net_tx_data; - ctx->connect = conn; + intf->rxd = intf->txd = ret; close(listener); return 0; -err_socket: +err: close(listener); -err_free: - free(conn); return ret; } -void net_disconnect(struct tfm_context *ctx) +static int net_disconnect(struct tfm_interface *intf) { - close(ctx->connect->fd); - - free(ctx->connect); - ctx->connect = NULL; + /* since rxd == txd, we close only rxd */ + return close(intf->rxd); } + +static struct tfm_interface_driver network = { + .name = "net", + .ops = { + .connect = net_connect, + .disconnect = net_disconnect, + .rx_data = net_rx_data, + .tx_data = net_tx_data, + }, +}; +INTERFACE_REGISTER(network) diff --git a/src/net.h b/src/net.h deleted file mode 100644 index dc96b4d..0000000 --- a/src/net.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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 index 8760e35..7d18f4e 100644 --- a/src/tfm.h +++ b/src/tfm.h @@ -16,25 +16,13 @@ #ifndef __TFM_H #define __TFM_H - #include #include #include #include "dfu.h" +#include "interface.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 { - struct tfm_connect *connect; - int port; -}; - -int thor_setup(struct tfm_context *tfm); -int thor_process(struct tfm_context *tfm, struct dfu_context *dfu); - +int thor_setup(struct tfm_interface *intf); +int thor_process(struct tfm_interface *intf, struct dfu_context *dfu); #endif diff --git a/src/thor.c b/src/thor.c index 1c255d2..426dd40 100644 --- a/src/thor.c +++ b/src/thor.c @@ -28,11 +28,12 @@ #include "tfm.h" #include "dfu.h" +#include "interface.h" #include "thor-proto.h" struct thor_context { struct rqt_pkt *rqt; - struct tfm_connect *intf; + struct tfm_interface *intf; struct dfu_context *dfu; struct dfu_entry *dfu_entry; @@ -52,10 +53,10 @@ static unsigned int _checkboard(void) static int thor_send_rsp(struct thor_context *tctx, struct res_pkt *rsp) { - struct tfm_connect *intf = tctx->intf; + struct tfm_interface *intf = tctx->intf; ssize_t n; - n = intf->tx_data(intf->fd, (void *)rsp, RES_PKT_SIZE); + n = tfm_interface_send(intf, (void *)rsp, RES_PKT_SIZE); if (n < sizeof(*rsp)) return -EIO; @@ -137,19 +138,19 @@ static int thor_process_rqt_cmd(struct thor_context *tctx) static void thor_send_data_rsp(struct thor_context *tctx, int ack, int count) { - struct tfm_connect *intf = tctx->intf; + struct tfm_interface *intf = tctx->intf; struct data_res_pkt rsp; rsp.ack = ack; rsp.cnt = count; - intf->tx_data(intf->fd, &rsp, DATA_RES_PKT_SIZE); + tfm_interface_send(intf, &rsp, DATA_RES_PKT_SIZE); } static int thor_download_head(struct thor_context *tctx, unsigned int packet_size) { - struct tfm_connect *intf = tctx->intf; + struct tfm_interface *intf = tctx->intf; struct dfu_entry *e = tctx->dfu_entry; uint64_t recv = 0; uint64_t total = tctx->file_size; @@ -160,7 +161,7 @@ thor_download_head(struct thor_context *tctx, unsigned int packet_size) tctx->buffer = buf = dfu_get_buffer(FLASH_UNIT_SIZE); while (total - recv >= packet_size) { - n = intf->rx_data(intf->fd, buf, packet_size); + n = tfm_interface_recv(intf, buf, packet_size); if (n < packet_size) return -EIO; @@ -181,7 +182,7 @@ thor_download_head(struct thor_context *tctx, unsigned int packet_size) tctx->remain = (total - recv) + (uint64_t)(buf - tctx->buffer); if ((total - recv) > 0) { - n = intf->rx_data(intf->fd, buf, packet_size); + n = tfm_interface_recv(intf, buf, packet_size); if (n < packet_size) return -EIO; recv += n; @@ -319,17 +320,17 @@ static int thor_do_request(struct thor_context *tctx) return ret; } -static int thor_handshake(struct tfm_connect *intf) +static int thor_handshake(struct tfm_interface *intf) { char buf[5]; ssize_t n; - n = intf->rx_data(intf->fd, buf, 4/* strlen("THOR") */); + n = tfm_interface_recv(intf, buf, 4/* strlen("THOR") */); if (n < 4) return -EIO; if (!strncmp(buf, "THOR", 4)) { - n = intf->tx_data(intf->fd, "ROHT", 4); + n = tfm_interface_send(intf, "ROHT", 4); if (n < 4) return -EIO; } else { @@ -340,9 +341,8 @@ static int thor_handshake(struct tfm_connect *intf) return 0; } -int thor_process(struct tfm_context *tfm, struct dfu_context *dfu) +int thor_process(struct tfm_interface *intf, struct dfu_context *dfu) { - struct tfm_connect *intf = tfm->connect; struct thor_context tctx; struct rqt_pkt rqt; ssize_t n; @@ -354,7 +354,7 @@ int thor_process(struct tfm_context *tfm, struct dfu_context *dfu) tctx.dfu = dfu; for (;;) { - n = intf->rx_data(intf->fd, &rqt, RQT_PKT_SIZE); + n = tfm_interface_recv(intf, &rqt, RQT_PKT_SIZE); if (n < sizeof(rqt)) { fprintf(stderr, "Failed to receive data from the host(%zd:%zu)", @@ -372,12 +372,12 @@ int thor_process(struct tfm_context *tfm, struct dfu_context *dfu) return 0; } -int thor_setup(struct tfm_context *tfm) +int thor_setup(struct tfm_interface *intf) { - if (!tfm->connect) { + if (!intf) { fprintf(stderr, "Invalid connection\n"); return -EINVAL; } - return thor_handshake(tfm->connect); + return thor_handshake(intf); } -- 2.7.4 From 62c1a507383c0858abf47f1f1ab8472ba42973d6 Mon Sep 17 00:00:00 2001 From: Dongwoo Lee Date: Tue, 17 Mar 2020 13:44:33 +0900 Subject: [PATCH 14/16] tfm: Get rid of unnecessary global header tfm.h Global tfm context is no more used, so global header tfm.h is neither required. This just removes unnecessary header, thus has no functional changes. Change-Id: I69daaaa255dea57676e04bdb0c8145cf4049ed87 Signed-off-by: Dongwoo Lee --- src/interface.c | 1 - src/main.c | 2 +- src/net.c | 2 +- src/thor.c | 1 - src/{tfm.h => thor.h} | 7 ++----- 5 files changed, 4 insertions(+), 9 deletions(-) rename src/{tfm.h => thor.h} (89%) diff --git a/src/interface.c b/src/interface.c index de6f559..69d2084 100644 --- a/src/interface.c +++ b/src/interface.c @@ -21,7 +21,6 @@ #include #include -#include "tfm.h" #include "interface.h" struct tfm_interface_context { diff --git a/src/main.c b/src/main.c index 141f165..0281c30 100644 --- a/src/main.c +++ b/src/main.c @@ -20,7 +20,7 @@ #include #include -#include "tfm.h" +#include "thor.h" #include "dfu.h" #include "interface.h" diff --git a/src/net.c b/src/net.c index 9de6741..6cd8587 100644 --- a/src/net.c +++ b/src/net.c @@ -14,6 +14,7 @@ * limitations under the License. */ +#include #include #include #include @@ -23,7 +24,6 @@ #include #include -#include "tfm.h" #include "interface.h" #define DEFAULT_PORT 23456 diff --git a/src/thor.c b/src/thor.c index 426dd40..6cb6984 100644 --- a/src/thor.c +++ b/src/thor.c @@ -26,7 +26,6 @@ #include #include -#include "tfm.h" #include "dfu.h" #include "interface.h" #include "thor-proto.h" diff --git a/src/tfm.h b/src/thor.h similarity index 89% rename from src/tfm.h rename to src/thor.h index 7d18f4e..7a88006 100644 --- a/src/tfm.h +++ b/src/thor.h @@ -14,11 +14,8 @@ * limitations under the License. */ -#ifndef __TFM_H -#define __TFM_H -#include -#include -#include +#ifndef __THOR_H +#define __THOR_H #include "dfu.h" #include "interface.h" -- 2.7.4 From a5f5989894617f51caf41550b5c760ed212ecfe4 Mon Sep 17 00:00:00 2001 From: Dongwoo Lee Date: Tue, 17 Mar 2020 13:53:32 +0900 Subject: [PATCH 15/16] interface: usb: Add support for USB interface USB interface is added and enabled by configuring gadget driver through configfs with support of libusbgx. Change-Id: I638bfa59e127f7896f8ed4b6fa11ba3d37d4548e Signed-off-by: Dongwoo Lee --- CMakeLists.txt | 4 +- packaging/initrd-flash.spec | 2 + scripts/flash-init.sh | 19 ++ src/usb.c | 492 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 515 insertions(+), 2 deletions(-) create mode 100644 src/usb.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 9fcc572..7c33f4a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,10 +6,10 @@ SET(CMAKE_EXE_LINKER_FLAGS "-pie") FIND_PACKAGE(Threads REQUIRED) -ADD_EXECUTABLE(${PROJECT_NAME} src/main.c src/thor.c src/dfu.c src/interface.c src/net.c) +ADD_EXECUTABLE(${PROJECT_NAME} src/main.c src/thor.c src/dfu.c src/interface.c src/net.c src/usb.c) INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/src) -TARGET_LINK_LIBRARIES(flash-manager "${CMAKE_THREAD_LIBS_INIT}" blkid) +TARGET_LINK_LIBRARIES(flash-manager "${CMAKE_THREAD_LIBS_INIT}" blkid usbgx) INSTALL(TARGETS ${PROJECT_NAME} DESTINATION bin) diff --git a/packaging/initrd-flash.spec b/packaging/initrd-flash.spec index d6dc741..a2b1889 100644 --- a/packaging/initrd-flash.spec +++ b/packaging/initrd-flash.spec @@ -8,6 +8,7 @@ Source0: %{name}-%{version}.tar.gz ExclusiveArch: %{arm} BuildRequires: cmake BuildRequires: libblkid-devel +BuildRequires: pkgconfig(libusbgx) Requires: util-linux Requires: bash @@ -16,6 +17,7 @@ Requires: fm-data Requires: module-init-tools Requires: wpa_supplicant Requires: dnsmasq +Requires: libusbgx %description Provide kernel-based target image downloader. diff --git a/scripts/flash-init.sh b/scripts/flash-init.sh index 799e80a..6473dd7 100755 --- a/scripts/flash-init.sh +++ b/scripts/flash-init.sh @@ -3,6 +3,8 @@ INFORM_FILE=reboot-param.bin INFORM_MOUNT_PATH=/mnt/inform TMP_PATH=/tmp +SYSFS_PATH=/sys +CONFIGFS_PATH=/sys/kernel/config BLKID="/usr/sbin/blkid" FLASH_MANAGER="/usr/bin/flash-manager" @@ -33,6 +35,22 @@ clear_bootmode() { } #------------------------------------------------ +# setup_usb +#------------------------------------------------ +setup_usb() { + SYSFS=$($MOUNT | $GREP sysfs) + if [ -z "$SYSFS" ]; then + $MOUNT -t sysfs none $SYSFS_PATH + fi + + CONFIGFS=$($MOUNT | $GREP configfs) + if [ -z "$CONFIGFS" ]; then + $MOUNT -t configfs none $CONFIGFS_PATH + fi +} + + +#------------------------------------------------ # Copy Module and Firmware file #------------------------------------------------ setup_wireless() { @@ -126,6 +144,7 @@ do_flash() { #------------------------------------------------ setup_wired_nic setup_wireless +setup_usb clear_bootmode print_info do_flash diff --git a/src/usb.c b/src/usb.c new file mode 100644 index 0000000..f7920bb --- /dev/null +++ b/src/usb.c @@ -0,0 +1,492 @@ +/* + * 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 + +#include +#include + +#include "interface.h" + +#define TFMFFS_PATH "/dev/tfmffs" +#define TFMFFS_GADGET "g1" +#define TFMFFS_DEV "tfm" +#define TFMFFS_BIND_LINK "tfm.ffs" +#define TFMFFS_VID 0x04e8 +#define TFMFFS_PID 0x685d + +enum { + TFMFFS_EP0, + TFMFFS_EP_INT_IN, + TFMFFS_EP_BULK_IN, + TFMFFS_EP_BULK_OUT, + TFMFFS_EP_MAX, +}; + +struct usb_context { + usbg_state *usbg; + pthread_t ep0_thread; + int eps[TFMFFS_EP_MAX]; + + int enabled; + + pthread_mutex_t mutex; + pthread_cond_t ready; +}; + +struct usbg_gadget_attrs g_attrs = { + .idVendor = TFMFFS_VID, + .idProduct = TFMFFS_PID, +}; + +struct usbg_gadget_strs g_strs = { + .serial = "Unavailable", /* Serial number */ + .manufacturer = "Tizen", /* Manufacturer */ + .product = "iot-refboard" /* Product string */ +}; + +struct usbg_config_strs c_strs = { + .configuration = "Conf 1" +}; + +/******************** Descriptors and Strings *******************************/ + +static const struct { + struct usb_functionfs_descs_head header; + struct { + struct usb_interface_descriptor acm; + struct usb_endpoint_descriptor_no_audio int_in; + struct usb_interface_descriptor intf; + struct usb_endpoint_descriptor_no_audio bulk_in; + struct usb_endpoint_descriptor_no_audio bulk_out; + } __attribute__ ((__packed__)) fs_descs, hs_descs; +} __attribute__ ((__packed__)) descriptors = { + .header = { + .magic = __cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC), + .length = __cpu_to_le32(sizeof(descriptors)), + .fs_count = __cpu_to_le32(5), + .hs_count = __cpu_to_le32(5), + }, + .fs_descs = { + .acm = { + .bLength = sizeof(descriptors.fs_descs.intf), + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_COMM, + .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, + .bInterfaceProtocol = USB_CDC_ACM_PROTO_AT_V25TER, + .iInterface = 1, + }, + .int_in = { + .bLength = sizeof(descriptors.fs_descs.bulk_in), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 3 | USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + }, + .intf = { + .bLength = sizeof(descriptors.fs_descs.intf), + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 1, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_CDC_DATA, + .iInterface = 1, + }, + .bulk_in = { + .bLength = sizeof(descriptors.fs_descs.bulk_in), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 1 | USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + }, + .bulk_out = { + .bLength = sizeof(descriptors.fs_descs.bulk_out), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 2 | USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + }, + }, + .hs_descs = { + .acm = { + .bLength = sizeof(descriptors.fs_descs.intf), + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_COMM, + .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, + .bInterfaceProtocol = USB_CDC_ACM_PROTO_AT_V25TER, + .iInterface = 1, + }, + .int_in = { + .bLength = sizeof(descriptors.fs_descs.bulk_in), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 3 | USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __cpu_to_le16(16), + .bInterval = 10, + }, + .intf = { + .bLength = sizeof(descriptors.hs_descs.intf), + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 1, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_CDC_DATA, + .iInterface = 1, + }, + .bulk_in = { + .bLength = sizeof(descriptors.hs_descs.bulk_in), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 1 | USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __cpu_to_le16(512), + }, + .bulk_out = { + .bLength = sizeof(descriptors.hs_descs.bulk_out), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 2 | USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __cpu_to_le16(512), + }, + }, +}; + +#define STR_INTERFACE "TIZEN USB DRIVER" + +static const struct { + struct usb_functionfs_strings_head header; + struct { + __le16 code; + const char str1[sizeof(STR_INTERFACE)]; + } __attribute__ ((__packed__)) lang0; +} __attribute__ ((__packed__)) strings = { + .header = { + .magic = __cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC), + .length = __cpu_to_le32(sizeof(strings)), + .str_count = __cpu_to_le32(1), + .lang_count = __cpu_to_le32(1), + }, + .lang0 = { + __cpu_to_le16(0x0409), /* en-us */ + STR_INTERFACE, + }, +}; + +static ssize_t usb_rx_data(int fd, void *buf, ssize_t len) +{ + return read(fd, buf, len); +} + +static ssize_t usb_tx_data(int fd, void *buf, ssize_t len) +{ + return write(fd, buf, len); +} + +static void *ep0_handler(void *data) +{ + struct usb_context *ctx = data; + struct usb_functionfs_event evt; + int ret, ep0 = ctx->eps[TFMFFS_EP0]; + + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + + for (;;) { + ret = read(ep0, &evt, sizeof(evt)); + if (ret <= 0) + goto err; + + switch (evt.type) { + case FUNCTIONFS_SETUP: + { + struct usb_ctrlrequest *ctrl = &evt.u.setup; + + if (ctrl->bRequestType & USB_DIR_IN) { + if (write(ep0, NULL, 0) < 0) + goto err; + } else { /* ctrl->bRequestType & USB_DIR_OUT */ + switch (ctrl->bRequest) { + /* handle CDC ACM setup request */ + case USB_CDC_REQ_SET_LINE_CODING: + { + int value = __le16_to_cpu(ctrl->wLength); + char *buf; + + buf = malloc(value); + if (!buf) + goto err; + + if (read(ep0, buf, value) < 0) { + free(buf); + goto err; + } + free(buf); + break; + } + case USB_CDC_REQ_SET_CONTROL_LINE_STATE: + { + int buf; + + if (read(ep0, &buf, sizeof(buf)) < 0) + goto err; + break; + } + default: + if (read(ep0, NULL, 0) < 0) + goto err; + } + } + break; + } + case FUNCTIONFS_ENABLE: + pthread_mutex_lock(&ctx->mutex); + ctx->enabled = 1; + pthread_mutex_unlock(&ctx->mutex); + pthread_cond_signal(&ctx->ready); + break; + case FUNCTIONFS_DISABLE: + case FUNCTIONFS_BIND: + case FUNCTIONFS_UNBIND: + default: + /* do nothing */ + break; + } + } +err: + fprintf(stderr, "Error occurs while handling ep0 event"); + return NULL; +} + +static int usb_setup_ffs_daemon(struct usb_context *ctx) +{ + char *ep_path; + int *ep = ctx->eps; + int ep_path_len = strlen(TFMFFS_PATH) + 4 /* "/ep#" */ + 1 /* '\0' */; + int i, ret; + + ep_path = malloc(ep_path_len); + if (!ep_path) + return -ENOMEM; + + snprintf(ep_path, ep_path_len, TFMFFS_PATH"/ep%d", TFMFFS_EP0); + + ep[TFMFFS_EP0] = open(ep_path, O_RDWR); + if (ep[TFMFFS_EP0] < 0) { + fprintf(stderr, "failed to open ffs ep0\n"); + ret = -ENOENT; + goto err_free; + } + + if (write(ep[TFMFFS_EP0], &descriptors, sizeof(descriptors)) < 0) { + fprintf(stderr, "failed to write descriptors\n"); + ret = -EIO; + goto err_close; + } + if (write(ep[TFMFFS_EP0], &strings, sizeof(strings)) < 0) { + fprintf(stderr, "failed to write strings\n"); + ret = -EIO; + goto err_close; + } + + for (i = 1; i < TFMFFS_EP_MAX; i++) { + snprintf(ep_path, ep_path_len, TFMFFS_PATH"/ep%d", i); + + ep[i] = open(ep_path, O_RDWR); + if (ep[i] < 0) { + fprintf(stderr, "failed to open ffs ep%d\n", i); + /* close opened endpoints */ + for (i -= 1; i >= 1; i--) + close(ep[i]); + ret = -EIO; + goto err_close; + } + } + + pthread_mutex_init(&ctx->mutex, NULL); + pthread_cond_init(&ctx->ready, NULL); + + pthread_create(&ctx->ep0_thread, NULL, ep0_handler, ctx); + pthread_detach(ctx->ep0_thread); + + free(ep_path); + + return 0; + +err_close: + close(ep[TFMFFS_EP0]); + +err_free: + free(ep_path); + + return ret; +} + +static void usb_cleanup_ffs_daemon(struct usb_context *ctx) +{ + int *ep = ctx->eps; + + pthread_cancel(ctx->ep0_thread); + close(ep[TFMFFS_EP0]); +} + +static int mount_ffs(void) +{ + int ret; + + ret = mkdir(TFMFFS_PATH, 0770); + if (ret < 0) { + fprintf(stderr, "Failed to create ffs directory\n"); + return ret; + } + + ret = mount("tfm", TFMFFS_PATH, "functionfs", 0, NULL); + if (ret < 0) { + fprintf(stderr, "Failed to mount ffs function\n"); + rmdir(TFMFFS_PATH); + } + + return ret; +} + +static void umount_ffs(void) +{ + umount(TFMFFS_PATH); + rmdir(TFMFFS_PATH); +} + +static int usb_connect(struct tfm_interface *intf) +{ + struct usb_context *ctx; + usbg_state *s; + usbg_gadget *g; + usbg_config *c; + usbg_function *f; + int ret; + + ctx = (struct usb_context *)malloc(sizeof(*ctx)); + if (!ctx) + return -ENOMEM; + + ret = usbg_init("/sys/kernel/config", &s); + if (ret < 0) { + fprintf(stderr, "Failed to init USB gadget\n"); + goto err_freectx; + } + + ret = usbg_create_gadget(s, TFMFFS_GADGET, &g_attrs, &g_strs, &g); + if (ret < 0) { + fprintf(stderr, "Failed to create USB gadget\n"); + goto err_cleanup; + } + + ret = usbg_create_function(g, USBG_F_FFS, TFMFFS_DEV, NULL, &f); + if (ret < 0) { + fprintf(stderr, "Failed to create USB function\n"); + goto err_cleanup; + } + + ret = usbg_create_config(g, 1, NULL, NULL, &c_strs, &c); + if (ret < 0) { + fprintf(stderr, "Failed to create USB config\n"); + goto err_cleanup; + } + + ret = usbg_add_config_function(c, TFMFFS_BIND_LINK, f); + if (ret < 0) { + fprintf(stderr, "Failed to bind USB function\n"); + goto err_cleanup; + } + + if (mount_ffs() < 0) { + fprintf(stderr, "Failed to mount functionfs\n"); + goto err_cleanup; + } + + if (usb_setup_ffs_daemon(ctx) < 0) { + fprintf(stderr, "Failed to setup ffs daemon\n"); + goto err_umount; + } + + ret = usbg_enable_gadget(g, DEFAULT_UDC); + if (ret < 0) { + fprintf(stderr, "Failed to enable USB gadget\n"); + goto err_ffs; + } + + pthread_mutex_lock(&ctx->mutex); + while (!ctx->enabled) + pthread_cond_wait(&ctx->ready, &ctx->mutex); + pthread_mutex_unlock(&ctx->mutex); + + ctx->usbg = s; + + intf->txd = ctx->eps[TFMFFS_EP_BULK_IN]; + intf->rxd = ctx->eps[TFMFFS_EP_BULK_OUT]; + intf->priv = (void *)ctx; + + return 0; + +err_ffs: + usb_cleanup_ffs_daemon(ctx); + +err_umount: + umount_ffs(); + +err_cleanup: + usbg_cleanup(s); + +err_freectx: + free(ctx); + + return -EINVAL; +} + +static int usb_disconnect(struct tfm_interface *intf) +{ + struct usb_context *ctx = intf->priv; + int *ep = ctx->eps; + int i; + + for (i = 1; i < TFMFFS_EP_MAX; i++) + close(ep[i]); + + usb_cleanup_ffs_daemon(ctx); + umount_ffs(); + if (ctx->usbg) + usbg_cleanup(ctx->usbg); + + return 0; +} + +static struct tfm_interface_driver usb = { + .name = "usb", + .ops = { + .connect = usb_connect, + .disconnect = usb_disconnect, + .rx_data = usb_rx_data, + .tx_data = usb_tx_data, + }, +}; +INTERFACE_REGISTER(usb) -- 2.7.4 From fb90bcce4ebf630e81f026a394206f2582ea622a Mon Sep 17 00:00:00 2001 From: Dongwoo Lee Date: Tue, 17 Mar 2020 13:55:56 +0900 Subject: [PATCH 16/16] interface: Choose interface after thor handshake To prevent that interface blocks other connection before host actually start transferring, connected interface is selected after handshake is done. Change-Id: I6b8587898b4dae83ffeb9e4330d379fa723e98d1 Signed-off-by: Dongwoo Lee --- src/interface.c | 14 ++++++++++++++ src/main.c | 6 ------ src/thor.c | 12 +----------- src/thor.h | 2 +- 4 files changed, 16 insertions(+), 18 deletions(-) diff --git a/src/interface.c b/src/interface.c index 69d2084..027a3fc 100644 --- a/src/interface.c +++ b/src/interface.c @@ -22,6 +22,7 @@ #include #include "interface.h" +#include "thor.h" struct tfm_interface_context { struct tfm_interface *connected; @@ -96,6 +97,19 @@ static void *connect_thread_main(void *ptr) return NULL; } + fprintf(stdout, "interface %s is ready.\n", intf->name); + + /* + * Each interface can handshake at the same time, but only one + * interface is used for flash to prevent data corruption. + */ + ret = thor_handshake(intf); + if (ret < 0) { + fprintf(stderr, "failed to handshake: %s\n", intf->name); + intf->ops->disconnect(intf); + return NULL; + } + pthread_mutex_lock(&ictx->mutex); if (ictx->connected) { pthread_mutex_unlock(&ictx->mutex); diff --git a/src/main.c b/src/main.c index 0281c30..7737e5e 100644 --- a/src/main.c +++ b/src/main.c @@ -100,12 +100,6 @@ int _main(int argc, char *argv[]) goto out_dfuexit; } - ret = thor_setup(intf); - if (ret < 0) { - ret = -1; - goto out_disconn; - } - ret = thor_process(intf, dfu); if (ret < 0) { ret = -1; diff --git a/src/thor.c b/src/thor.c index 6cb6984..ea8918e 100644 --- a/src/thor.c +++ b/src/thor.c @@ -319,7 +319,7 @@ static int thor_do_request(struct thor_context *tctx) return ret; } -static int thor_handshake(struct tfm_interface *intf) +int thor_handshake(struct tfm_interface *intf) { char buf[5]; ssize_t n; @@ -370,13 +370,3 @@ int thor_process(struct tfm_interface *intf, struct dfu_context *dfu) return 0; } - -int thor_setup(struct tfm_interface *intf) -{ - if (!intf) { - fprintf(stderr, "Invalid connection\n"); - return -EINVAL; - } - - return thor_handshake(intf); -} diff --git a/src/thor.h b/src/thor.h index 7a88006..c24fcc5 100644 --- a/src/thor.h +++ b/src/thor.h @@ -20,6 +20,6 @@ #include "dfu.h" #include "interface.h" -int thor_setup(struct tfm_interface *intf); +int thor_handshake(struct tfm_interface *intf); int thor_process(struct tfm_interface *intf, struct dfu_context *dfu); #endif -- 2.7.4