summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYoungJin Lee <yj0701.lee@samsung.com>2012-11-02 19:55:18 +0900
committerYoungJin Lee <yj0701.lee@samsung.com>2012-11-02 19:55:18 +0900
commit4b15e5f2e0f5d910dad4e71956de0fa0122b4929 (patch)
tree6f4516b39eede691d95fe86fe28491bd75e70931
parent9e3ce8bc980d2eed0c544c4520d75df9adb268ac (diff)
downloadlthor-4b15e5f2e0f5d910dad4e71956de0fa0122b4929.tar.gz
lthor-4b15e5f2e0f5d910dad4e71956de0fa0122b4929.tar.bz2
lthor-4b15e5f2e0f5d910dad4e71956de0fa0122b4929.zip
initial commit of lthor
Change-Id: Ifd62a81919f2a88a22b38627dd467d53c5408823
-rwxr-xr-xCMakeLists.txt52
-rwxr-xr-xlthor.c1162
-rw-r--r--thor-proto.h78
3 files changed, 1292 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100755
index 0000000..25d7c8f
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,52 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
+PROJECT(lthor C)
+
+SET(SRCS
+ lthor.c
+)
+
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/.)
+
+SET(VENDOR "samsung")
+SET(PACKAGE ${PROJECT_NAME})
+SET(PKGNAME "${PACKAGE}")
+SET(PREFIX ${CMAKE_INSTALL_PREFIX})
+SET(BINDIR "${PREFIX}/bin")
+
+SET(PACKAGE_VERSION "1.2")
+
+
+IF("${CMAKE_BUILD_TYPE}" STREQUAL "")
+ SET(CMAKE_BUILD_TYPE "Release")
+ENDIF("${CMAKE_BUILD_TYPE}" STREQUAL "")
+MESSAGE("Build type: ${CMAKE_BUILD_TYPE}")
+
+
+INCLUDE(FindPkgConfig)
+pkg_check_modules(pkgs REQUIRED
+ libarchive
+)
+
+FOREACH(flag ${pkgs_CFLAGS})
+ SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
+ENDFOREACH(flag)
+
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS}")
+SET(CMAKE_C_FLAGS_DEBUG "-O0 -g")
+SET(CMAKE_C_FLAGS_RELEASE "-O2")
+
+
+ADD_DEFINITIONS("-DVENDOR=\"${VENDOR}\"")
+ADD_DEFINITIONS("-DPACKAGE=\"${PACKAGE}\"")
+ADD_DEFINITIONS("-DPACKAGE_NAME=\"${PKGNAME}\"")
+ADD_DEFINITIONS("-DPREFIX=\"${PREFIX}\"")
+ADD_DEFINITIONS("-DPACKAGE_VERSION=\"${PACKAGE_VERSION}\"")
+
+#SET(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed")
+SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--as-needed")
+
+ADD_EXECUTABLE(${PROJECT_NAME} ${SRCS})
+TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${pkgs_LDFLAGS})
+
+
+INSTALL(TARGETS ${PROJECT_NAME} DESTINATION ${BINDIR})
diff --git a/lthor.c b/lthor.c
new file mode 100755
index 0000000..d756c23
--- /dev/null
+++ b/lthor.c
@@ -0,0 +1,1162 @@
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <assert.h>
+#include <sys/mman.h>
+#include <poll.h>
+#include <errno.h>
+#include <dirent.h>
+#include <libgen.h>
+
+#include <archive.h>
+#include <archive_entry.h>
+
+#include "thor-proto.h"
+
+#define USB_VENDOR_SAMSUNG "04e8"
+#define USB_DEVICE_Z100 "6601"
+#define USB_DEVICE_Z100_DEVGURU "6860"
+#define USB_DEVICE_DEVGURU "685d"
+#define USB_DEVICE_OLD_DOWNLOADER "1204"
+
+#define KB (1024)
+#define MB (1024*KB)
+#define DEFAULT_PKT_SIZE (1*MB)
+#define DEFAULT_TIMEOUT (60000) /* 1 minute */
+#define DEFAULT_PORT "/dev/ttyACM0"
+
+/* data abstraction */
+struct data_src {
+ size_t (*get_data_length)(struct data_src *src);
+ size_t (*get_data_block)(struct data_src *src, void *data, size_t len);
+ const char* (*get_data_name)(struct data_src *src);
+};
+
+int opt_verbose = 0;
+int opt_test = 0;
+size_t trans_unit_size = DEFAULT_PKT_SIZE;
+
+static void dump(const char *msg, void *bytes, int len)
+{
+ int i;
+
+ if (!opt_verbose)
+ return;
+
+ fprintf(stderr, "%s :", msg);
+ for (i=0; i<len; i++) {
+ if (i && (0==i%16))
+ fprintf(stderr, " ");
+ fprintf(stderr, "%02x%c", ((unsigned char*)bytes)[i],
+ ((len == i+1) || (i+1)%16==0) ? '\n': ' ');
+ }
+}
+
+static off_t file_size(const char *filename)
+{
+ struct stat st;
+ if (stat(filename, &st) < 0)
+ return -1;
+
+ return st.st_size;
+}
+
+int serial_read(int fd, void *addr, size_t len, int timeout)
+{
+ unsigned char *p = addr;
+ int n = 0, r;
+
+ while (n < len) {
+ struct pollfd fds;
+ fds.fd = fd;
+ fds.events = POLLIN;
+ fds.revents = 0;
+ r = poll(&fds, 1, timeout);
+ if (r < 0) {
+ fprintf(stderr, "line %d: poll failed\n", __LINE__);
+ return -1;
+ }
+
+ if (r == 0) {
+ if (opt_verbose)
+ fprintf(stderr, "line %d: timeout after reading %d bytes\n", __LINE__, n);
+ return n;
+ }
+
+ if (!(fds.revents & POLLIN)) {
+ fprintf(stderr, "line %d: poll returned non-read event %08x\n", __LINE__, fds.revents);
+ continue;
+ }
+
+ r = read(fd, &p[n], len - n);
+ if (r < 0) {
+ fprintf(stderr, "line %d: read failed\n", __LINE__);
+ return -1;
+ }
+
+ n += r;
+ }
+
+ return n;
+}
+
+static double timediff (struct timeval *atv, struct timeval *btv)
+{
+ double diff;
+
+ if (btv->tv_usec < atv->tv_usec) {
+ diff = btv->tv_sec - atv->tv_sec - 1;
+ diff += (double)(1*1000*1000 - atv->tv_usec +
+ btv->tv_usec)/(1000*1000);
+ } else {
+ diff = btv->tv_sec - atv->tv_sec;
+ diff += (double)(btv->tv_usec - atv->tv_usec)/(1000*1000);
+ }
+
+ return diff;
+}
+
+int send_chunks(int fd, struct data_src *senddata, size_t size)
+{
+ unsigned char *chunk;
+ struct data_res_pkt resp;
+ size_t n = 0;
+ int chunksize, chunk_number = 1;
+ struct timeval stv, etv, ltv;
+ int r;
+
+ chunk = malloc(trans_unit_size);
+ if (chunk == NULL) {
+ fprintf(stderr, "Unable to allocate memory.\n");
+ return -1;
+ }
+
+ if (opt_verbose)
+ fprintf(stderr, "Downloading: %zd bytes\n", size);
+
+ gettimeofday(&stv, NULL);
+ memcpy(&ltv, &stv, sizeof(struct timeval));
+
+ while (n < size) {
+ if (size - n >= trans_unit_size)
+ chunksize = trans_unit_size;
+ else
+ chunksize = size - n;
+
+ chunksize = senddata->get_data_block(senddata, chunk, chunksize);
+ if (chunksize <= 0) {
+ fprintf(stderr, "\nline %d: Broken tar archive - check download!\n", __LINE__);
+ exit(1);
+ }
+
+ if (opt_verbose)
+ fprintf(stderr, "read %d bytes\n", chunksize);
+
+ assert(chunksize > 0 && chunksize <= trans_unit_size);
+ memset(&chunk[chunksize], 0, trans_unit_size - chunksize);
+
+ if (opt_verbose)
+ fprintf(stderr, "sending %zd/%zd\n", n + chunksize, size);
+
+ r = write(fd, chunk, trans_unit_size);
+ if (r != trans_unit_size) {
+ fprintf(stderr, "line %d: failed to send data\n", __LINE__);
+ free(chunk);
+ return -1;
+ }
+
+ memset(&resp, 0, DATA_RES_PKT_SIZE);
+ r = serial_read(fd, &resp, DATA_RES_PKT_SIZE, DEFAULT_TIMEOUT);
+ if (r != DATA_RES_PKT_SIZE) {
+ fprintf(stderr, "line %d: read failed %d\n", __LINE__, r);
+ free(chunk);
+ return -1;
+ }
+
+ if (resp.cnt != chunk_number) {
+ printf("\nsending %zd/%zd : sequence wrong %08x, expected %08x\n",
+ n, size, resp.cnt, chunk_number);
+ //return -1;
+ }
+
+ n += chunksize;
+
+ if (opt_verbose) {
+ printf("response %08x %08x\n", resp.ack, resp.cnt);
+
+ } else {
+ int now = n/KB;
+ int total = size/KB;
+ char progress [4] = { '-', '\\', '|', '/' };
+ char c = progress[(now/30)%4];
+ double elaps;
+
+ gettimeofday(&etv, NULL);
+ if (n >= size) {
+ elaps = timediff(&stv, &etv);
+ fprintf(stderr, "\x1b[1A\x1b[16C%c sending %6dk/%6dk %3d%% block %-6d",
+ c, now, total, ((now*100)/total), chunk_number);
+ fprintf(stderr, " [avg %.2f MB/s]\n", (double)(size/elaps)/(MB));
+ } else {
+ elaps = timediff(&ltv, &etv);
+ memcpy(&ltv, &etv, sizeof(struct timeval));
+ fprintf(stderr, "\x1b[1A\x1b[16C%c sending %6dk/%6dk %3d%% block %-6d",
+ c, now, total, ((now*100)/total), chunk_number);
+ fprintf(stderr, " [%.2f MB/s]\n", (double)(chunksize/elaps)/(MB));
+ }
+ }
+ chunk_number++;
+ }
+
+ free(chunk);
+
+ return 0;
+}
+
+/*
+ * function that doesn't wait for response data, used by standard requests and
+ * REQ_PIT_DATA_START, which doesn't want a response
+ */
+int write_request(int fd, request_type req_id, int req_sub_id,
+ int *idata, int icnt, char **sdata, int scnt)
+{
+ struct rqt_pkt req;
+ int r, i;
+
+ assert(icnt <= sizeof(req.int_data)/sizeof(req.int_data[0]));
+ assert(icnt >= 0);
+ assert(scnt <= sizeof(req.str_data)/sizeof(req.str_data[0]));
+ assert(scnt >= 0);
+
+ memset(&req, 0, sizeof(req));
+
+ req.id = req_id;
+ req.sub_id = req_sub_id;
+
+ if (idata) {
+ for (i=0; i<icnt; i++)
+ req.int_data[i] = idata[i];
+ }
+
+ if (sdata) {
+ for (i=0; i<scnt; i++)
+ strcpy(req.str_data[i],sdata[i]);
+ }
+
+ dump("send", &req, 0x20);
+
+ r = write(fd, &req, RQT_PKT_SIZE);
+ if (r != RQT_PKT_SIZE) {
+ fprintf(stderr, "line %d: failed to send data r = %d\n",
+ __LINE__, r);
+ return -1;
+ }
+
+ return 0;
+}
+
+int send_request_timeout(int fd, request_type req_id, int req_sub_id,
+ int *idata, int icnt, char **sdata, int scnt, struct res_pkt *pres, int timeout)
+{
+ struct res_pkt resp;
+ int r;
+
+ r = write_request(fd, req_id, req_sub_id, idata, icnt, sdata, scnt);
+ if (r < 0) {
+ fprintf(stderr, "line %d: failed to write request r = %d\n",
+ __LINE__, r);
+ return -1;
+ }
+
+ r = serial_read(fd, &resp, sizeof resp, timeout);
+ if (r >= 0)
+ dump("recv", &resp, sizeof resp);
+ if (r != sizeof resp) {
+ fprintf(stderr, "line %d: failed to receive data r = %d\n",
+ __LINE__, r);
+ return -1;
+ }
+
+ if (pres)
+ memcpy(pres, &resp, RES_PKT_SIZE);
+
+ if (resp.ack != 0) {
+ fprintf(stderr, "line %d: ack reports fail. ack = %d\n",
+ __LINE__, resp.ack);
+ return -1;
+ }
+
+ return resp.ack;
+}
+
+int send_request(int fd, request_type req_id, int req_sub_id, int *idata, int icnt)
+{
+ return send_request_timeout(fd, req_id, req_sub_id, idata, icnt,
+ NULL, 0, NULL, DEFAULT_TIMEOUT);
+}
+
+int request_reboot(int fd)
+{
+ int r;
+ r = send_request(fd, RQT_CMD, RQT_CMD_REBOOT, NULL, 0);
+ if (r)
+ fprintf(stderr, "RQT_CMD_REBOOT, status = %08x\n", r);
+ return r;
+}
+
+int thor_handshake(int fd)
+{
+ char buffer[4];
+ int r;
+
+ r = write(fd, "THOR", 4);
+ if (r != 4) {
+ fprintf(stderr, "line %d: failed to write signature bytes\n",
+ __LINE__);
+ return -1;
+ }
+
+ r = serial_read(fd, buffer, 4, DEFAULT_TIMEOUT);
+ if (r != 4) {
+ fprintf(stderr, "line %d: failed to read signature bytes\n",
+ __LINE__);
+ return -1;
+ }
+
+ if (memcmp(buffer, "ROHT", 4)) {
+ fprintf(stderr, "line %d: signature byte mismatch\n", __LINE__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int getfile(const char *devpath, char *buffer, size_t bufsize)
+{
+ int fd, r = 0;
+
+ fd = open(devpath, O_RDONLY);
+ if (fd >= 0) {
+ r = read(fd, buffer, bufsize - 1);
+ if (r < 0)
+ r = 0;
+ close(fd);
+ }
+ else if (opt_verbose)
+ fprintf(stderr, "failed to open %s\n", devpath);
+ if (r && buffer[r - 1] == '\n')
+ r--;
+ buffer[r] = 0;
+ return r;
+}
+
+/*
+ * Given USB path, return tty name
+ * FIXME: probably should look up the /dev from device major:minor
+ * There seems to be two styles of device name:
+ * 1. /sys/bus/usb/devices/1-2/1-2\:2.0/tty/ttyACM0/dev
+ * 2. /sys/bus/usb/devices/1-2/1-2\:2.0/tty:ttyACM0
+ */
+char *device_from_usb_tty_directory(const char *usbpath)
+{
+ static char ttypath[0x400];
+ static char devpath[0x400];
+ char *ret = NULL;
+ DIR *d;
+
+ strcpy(ttypath, usbpath);
+ strcat(ttypath, "/tty");
+
+ d = opendir(ttypath);
+ if (d) {
+ if (opt_verbose)
+ fprintf(stderr, "listing %s:\n", ttypath);
+
+ /* device name is under tty directory */
+ while (!ret) {
+ struct dirent *de;
+
+ de = readdir(d);
+ if (!de)
+ break;
+
+ if (opt_verbose)
+ fprintf(stderr, "%s\n", de->d_name);
+
+ if (de->d_name[0] == '.')
+ continue;
+
+ strcpy(devpath, "/dev/");
+ strcat(devpath, de->d_name);
+
+ ret = devpath;
+ }
+ } else {
+ /* device name is in directory */
+ d = opendir(usbpath);
+ if (!d)
+ return NULL;
+
+ if (opt_verbose)
+ fprintf(stderr, "listing %s:\n", usbpath);
+
+ while (!ret) {
+ struct dirent *de;
+
+ de = readdir(d);
+ if (!de)
+ break;
+
+ if (opt_verbose)
+ fprintf(stderr, "%s\n", de->d_name);
+
+ if (strncmp(de->d_name, "tty:", 4))
+ continue;
+
+ strcpy(devpath, "/dev/");
+ strcat(devpath, &de->d_name[4]);
+
+ ret = devpath;
+ }
+ }
+
+ closedir(d);
+
+ if (ret) {
+ fprintf(stderr, "USB port is detected : %s\n\n", ret);
+ } else {
+ fprintf(stderr, "USB port is "
+ "\x1b[0;31;1mnot\x1b[0m detected !\n\n", ret);
+ }
+
+ return ret;
+}
+
+const char* find_usb_device(void)
+{
+ DIR *d;
+ char *p;
+ char buffer[11];
+ const char *dirname = "/sys/bus/usb/devices";
+ char usbpath[0x400];
+ char usbdir[0x40];
+ char *tty = NULL;
+
+ d = opendir(dirname);
+ if (!d)
+ return NULL;
+
+ while (1) {
+ struct dirent *de;
+
+ de = readdir(d);
+ if (!de)
+ break;
+
+ if (de->d_name[0] == '.')
+ continue;
+
+ strcpy(usbdir, de->d_name);
+ strcpy(usbpath, dirname);
+ strcat(usbpath, "/");
+ strcat(usbpath, usbdir);
+ strcat(usbpath, "/");
+ p = &usbpath[strlen(usbpath)];
+
+ /* match the vendor ID */
+ strcat(p, "idVendor");
+ getfile(usbpath, buffer, sizeof buffer);
+ if (opt_verbose)
+ fprintf(stderr, "vendorId = >%s< %zd\n", buffer,
+ strlen(buffer));
+ if (strcmp(buffer, USB_VENDOR_SAMSUNG))
+ continue;
+
+ /* match the product ID */
+ strcpy(p, "idProduct");
+ getfile(usbpath, buffer, sizeof buffer);
+ if (opt_verbose)
+ fprintf(stderr, "product = >%s< %zd\n", buffer,
+ strlen(buffer));
+
+ if (!strcmp(buffer, USB_DEVICE_OLD_DOWNLOADER))
+ fprintf(stderr, "Old bootloader detected!\n");
+
+ /* supported product ID */
+ if (strcmp(buffer, USB_DEVICE_Z100) &&
+ strcmp(buffer, USB_DEVICE_Z100_DEVGURU) &&
+ strcmp(buffer, USB_DEVICE_DEVGURU))
+ continue;
+
+ if (opt_verbose)
+ fprintf(stderr, "found %s:%s!\n", USB_VENDOR_SAMSUNG,
+ buffer);
+ closedir(d);
+
+ p[0] = 0x00;
+ d = opendir(usbpath);
+ if (opt_verbose)
+ fprintf(stderr, "at %s\n", usbpath);
+ while ((de = readdir(d))) {
+ if (strlen(de->d_name) < strlen(usbdir))
+ continue;
+ if (de->d_type != DT_DIR)
+ continue;
+ if (strncmp(de->d_name, usbdir, strlen(usbdir)))
+ continue;
+
+ strcpy(p, de->d_name);
+ if (opt_verbose)
+ fprintf(stderr, "search for tty on %s\n", usbpath);
+ tty = device_from_usb_tty_directory(usbpath);
+ if (tty)
+ break;
+ }
+
+ closedir(d);
+ if ((opt_verbose) && (!tty))
+ fprintf(stderr, "idVendor and idProduct are matched"
+ "but no tty description\n");
+
+ return tty;
+ }
+
+ closedir(d);
+
+ if (opt_verbose)
+ fprintf(stderr, "No USB device found with matching "
+ "idVendor and idProduct\n" );
+
+ return NULL;
+}
+
+
+int wait_and_open_port(const char *portname)
+{
+ int once = 0;
+ int fd;
+ int r;
+ struct termios tios;
+ const char *dev;
+
+ if (opt_test)
+ return open("/dev/null", O_RDWR);
+
+ while (1) {
+ if (!portname)
+ dev = find_usb_device();
+ else
+ dev = portname;
+
+ if (dev) {
+ fd = open(dev, O_RDWR);
+ if (fd == -1) {
+ perror("port open error!!\n");
+ return -1;
+ }
+ break;
+ }
+
+ if (!once) {
+ printf("\nUSB port is not detected yet... \n");
+ printf("Make sure phone(device) should be in a download mode \n");
+ printf("before connecting phone to PC with USB cable.\n");
+ printf("(How to enter download mode : press <volume-down> + <power> key)\n\n");
+ once = 1;
+ }
+
+ sleep(1);
+ }
+
+ r = tcgetattr(fd, &tios);
+ if (r < 0) {
+ fprintf(stderr, "line %d: tcgetattr failed\n", __LINE__);
+ return -1;
+ }
+
+ /*
+ * Firmware BUG alert!!!
+ * Flow control (RTS/CTS) should be disabled, because
+ * the firmware doesn't deal with it properly.
+ */
+ cfmakeraw(&tios);
+ r = tcsetattr(fd, TCSANOW, &tios);
+ if (r < 0) {
+ fprintf(stderr, "line %d: tcsetattr failed\n", __LINE__);
+ return -1;
+ }
+
+ r = tcflush(fd, TCIOFLUSH);
+ if (r < 0) {
+ fprintf(stderr, "line %d: tcflush failed\n", __LINE__);
+ return -1;
+ }
+
+ r = thor_handshake(fd);
+ if (r < 0) {
+ fprintf(stderr, "line %d: handshake failed\n", __LINE__);
+ return -1;
+ }
+
+ return fd;
+}
+
+/*
+ * Write a data source (file) into a partition
+ *
+ * data_src abstraction provided so sending a single file
+ * from a non-tar source is possible one day
+ */
+int download_single_file(int fd, struct data_src *senddata, int filetype)
+{
+ int r;
+ size_t filesize;
+ const char *filename;
+ struct res_pkt resp;
+ int32_t int_data[2];
+
+ filesize = senddata->get_data_length(senddata);
+ filename = senddata->get_data_name(senddata);
+
+ int_data[0] = filetype;
+ int_data[1] = filesize;
+ r = send_request_timeout(fd, RQT_DL, RQT_DL_FILE_INFO, int_data, 2,
+ (char **)&filename, 1, &resp, DEFAULT_TIMEOUT);
+ if (r < 0) {
+ fprintf(stderr, "RQT_DL_FILE_INFO, status = %08x\n", r);
+ return -1;
+ }
+ trans_unit_size = resp.int_data[0];
+
+ r = send_request(fd, RQT_DL, RQT_DL_FILE_START, NULL, 0);
+ if (r < 0) {
+ fprintf(stderr, "RQT_DL_FILE_START, status = %08x\n", r);
+ return -1;
+ }
+
+ r = send_chunks(fd, senddata, filesize);
+ if (r < 0) {
+ fprintf(stderr, "send_chunks(), status = %08x\n", r);
+ return -1;
+ }
+
+ r = send_request(fd, RQT_DL, RQT_DL_FILE_END, NULL, 0);
+ if (r < 0) {
+ fprintf(stderr, "RQT_DL_FILE_END, status = %08x\n", r);
+ return -1;
+ }
+
+ return 0;
+}
+
+struct file_data_src {
+ struct data_src src;
+ int fd;
+ const char* filename;
+ size_t filesize;
+};
+
+size_t file_get_data_length(struct data_src *src)
+{
+ struct file_data_src *filedata = (struct file_data_src *) src;
+
+ return filedata->filesize;
+}
+
+size_t file_get_data_block(struct data_src *src, void *data, size_t len)
+{
+ struct file_data_src *filedata = (struct file_data_src *) src;
+
+ return read(filedata->fd, data, len);
+}
+
+const char *file_get_data_name(struct data_src *src)
+{
+ struct file_data_src *filedata = (struct file_data_src *) src;
+
+ return filedata->filename;
+}
+
+int filedata_open(struct file_data_src *filedata, const char *filename)
+{
+ int r;
+ char *basefile;
+
+ if (!filedata)
+ return -1;
+
+ memset((void*)filedata, 0x00, sizeof(struct file_data_src));
+
+ r = open(filename, O_RDONLY);
+ if (r < 0)
+ return r;
+
+ filedata->fd = r;
+
+ /* According to the man page basename() might modify the argument or
+ * return a pointer to statically allocated memory. Thus two strdup()s */
+ basefile = strdup(filename);
+ filedata->filename = strdup(basename(basefile));
+ free(basefile);
+
+ filedata->filesize = lseek(filedata->fd, 0, SEEK_END);
+ lseek(filedata->fd, 0, SEEK_SET);
+
+ filedata->src.get_data_length = &file_get_data_length;
+ filedata->src.get_data_block = &file_get_data_block;
+ filedata->src.get_data_name = &file_get_data_name;
+
+ return r;
+}
+
+int filedata_close(struct file_data_src *filedata)
+{
+ close(filedata->fd);
+ free((void *)filedata->filename);
+ return 0;
+}
+
+struct tar_data_src {
+ struct data_src src;
+ struct archive *ar;
+ struct archive_entry *ae;
+};
+
+size_t te_get_data_length(struct data_src *src)
+{
+ struct tar_data_src *tardata = (struct tar_data_src *) src;
+
+ return archive_entry_size(tardata->ae);
+}
+
+size_t te_get_data_block(struct data_src *src, void *data, size_t len)
+{
+ struct tar_data_src *tardata = (struct tar_data_src *) src;
+
+ return archive_read_data(tardata->ar, data, len);
+}
+
+const char *te_get_data_name(struct data_src *src)
+{
+ struct tar_data_src *tardata = (struct tar_data_src *) src;
+
+ return archive_entry_pathname(tardata->ae);
+}
+
+int tardata_open(struct tar_data_src *tardata, const char *tarfile)
+{
+ int r;
+
+ /* open the tar archive */
+ tardata->ar = archive_read_new();
+
+ archive_read_support_format_tar(tardata->ar);
+ archive_read_support_compression_gzip(tardata->ar);
+ archive_read_support_compression_bzip2(tardata->ar);
+
+ if (!strcmp(tarfile, "-"))
+ r = archive_read_open_FILE(tardata->ar, stdin);
+ else
+ r = archive_read_open_filename(tardata->ar, tarfile, 512);
+
+ if (r) {
+ fprintf(stderr, "line %d: failed to open %s\n", __LINE__, tarfile);
+ return r;
+ }
+
+ tardata->src.get_data_length = &te_get_data_length;
+ tardata->src.get_data_block = &te_get_data_block;
+ tardata->src.get_data_name = &te_get_data_name;
+ tardata->ae = NULL;
+
+ return r;
+}
+
+const char *tardata_next(struct tar_data_src *tardata)
+{
+ int64_t partsize;
+ int r;
+
+ r = archive_read_next_header(tardata->ar, &tardata->ae);
+ if (r == ARCHIVE_EOF)
+ return NULL;
+
+ if (r != ARCHIVE_OK) {
+ fprintf(stderr, "line %d: archive_read_next_header error %d\n",
+ __LINE__, r);
+ return NULL;
+ }
+
+ partsize = archive_entry_size(tardata->ae);
+ if (partsize > SIZE_MAX) {
+ /*
+ * FIXME: fix source (and maybe firmware) to deal with files
+ * bigger than 4G
+ */
+ fprintf(stderr, "line %d: Large file %llx (>4GB) is not"
+ " supported\n", __LINE__, (unsigned long long)
+ partsize);
+ return NULL;
+ }
+
+ return archive_entry_pathname(tardata->ae);
+}
+
+int tardata_close(struct tar_data_src *tardata)
+{
+ archive_read_close(tardata->ar);
+ archive_read_finish(tardata->ar);
+ return 0;
+}
+
+int get_entry_size_in_tar(const char *tarfile, unsigned long long *total)
+{
+ struct tar_data_src tardata;
+ const char *filename;
+ int r;
+
+ r = tardata_open(&tardata, tarfile);
+ if (r < 0) {
+ fprintf(stderr, "line %d: failed to open %s\n", __LINE__,
+ tarfile);
+ return r;
+ }
+
+ *total = 0;
+
+ while (1) {
+ size_t size = 0;
+
+ filename = tardata_next(&tardata);
+ if (!filename)
+ break;
+
+ size = tardata.src.get_data_length(&tardata.src);
+ printf("[\x1b[0;32;1m%s\x1b[0m]\t%zuk\n", filename, size/KB);
+
+ *total += size;
+ }
+
+ tardata_close(&tardata);
+
+ return 0;
+}
+
+int download_pitfile(int fd, const char *pitfile)
+{
+ struct file_data_src filedata;
+ int r;
+
+ r = filedata_open(&filedata, pitfile);
+ if (r < 0) {
+ fprintf(stderr, "line %d: failed to open %s\n", __LINE__, pitfile);
+ return r;
+ }
+
+
+ printf("[\x1b[0;32;1m%s\x1b[0m]\n", pitfile);
+ r = download_single_file(fd, &filedata.src, BINARY_TYPE_PIT);
+ if (r < 0) {
+ fprintf(stderr, "line %d: failed to download %s\n", __LINE__,
+ pitfile);
+ return r;
+ }
+
+ fprintf(stderr, "\n%s completed\n", pitfile);
+
+ filedata_close(&filedata);
+
+ return r;
+}
+
+int download_single_tarfile(int fd, const char *tarfile)
+{
+ struct tar_data_src tardata;
+ const char *filename;
+ int r;
+
+ r = tardata_open(&tardata, tarfile);
+ if (r < 0) {
+ fprintf(stderr, "line %d: failed to open %s\n", __LINE__, tarfile);
+ return r;
+ }
+
+ while (1) {
+ filename = tardata_next(&tardata);
+ if (!filename)
+ break;
+
+ printf("[\x1b[0;32;1m%s\x1b[0m]\n", filename);
+ r = download_single_file(fd, &tardata.src, BINARY_TYPE_NORMAL);
+ if (r < 0) {
+ fprintf(stderr, "line %d: failed to download %s\n",
+ __LINE__, filename);
+ break;
+ }
+ }
+
+ fprintf(stderr, "\n%s completed\n", tarfile);
+
+ tardata_close(&tardata);
+
+ return r;
+}
+
+int process_download(const char *portname, const char *pitfile, char **tarfilelist)
+{
+ int r;
+ char **tfl;
+ off_t pit_length = 0;
+ int fd;
+ unsigned long long total;
+
+ /* now connect to the target */
+ fd = wait_and_open_port(portname);
+ if (fd < 0) {
+ fprintf(stderr, "line %d: failed to open port %s\n", __LINE__,
+ portname);
+ return -1;
+ }
+
+ total = 0;
+ tfl = tarfilelist;
+ while (tfl && *tfl) {
+ unsigned long long len = 0;
+ printf("\x1b[0;33;1m%s :\x1b[0m\n", *tfl);
+ if (get_entry_size_in_tar(*tfl, &len) < 0) {
+ perror("Error");
+ return -1;
+ }
+ total += len;
+ tfl++;
+ }
+
+ if (pitfile) {
+ pit_length = file_size(pitfile);
+ if (pit_length < 0) {
+ fprintf(stderr, "line %d: failed to get pit length\n"
+ , __LINE__);
+ return -1;
+ }
+ total += pit_length;
+ printf("\x1b[0;33;1m%s : \x1b[0m%zuk\n", pitfile,
+ (size_t)pit_length/KB);
+ }
+ printf("-------------------------\n");
+ printf("\t\x1b[0;33;1mtotal\x1b[0m :\t%.2fMB\n\n", (double)total/MB);
+
+ /* for updating progress bar in the target */
+ r = send_request(fd, RQT_DL, RQT_DL_INIT, (int*)&total, 1);
+ /*
+ * FIXME : if total > SIZE_MAX then DL_INIT must change it's protocol to
+ * send/receive 64bit size data.
+ */
+ if (r) {
+ fprintf(stderr, "RQT_DL_INIT, status = %08x\n", r);
+ return -1;
+ }
+
+ if (pitfile) {
+ fprintf(stderr, "\nDownload PIT file : %s\n\n", pitfile);
+ r = download_pitfile(fd, pitfile);
+ if (r < 0) {
+ fprintf(stderr, "failed to download %s\n", pitfile);
+ close(fd);
+ return r;
+ }
+ }
+
+ while (tarfilelist && *tarfilelist) {
+ fprintf(stderr, "\nDownload files from %s\n\n", *tarfilelist);
+ r = download_single_tarfile(fd, *tarfilelist);
+ if (r < 0) {
+ fprintf(stderr, "failed to download %s\n", *tarfilelist);
+ close(fd);
+ return r;
+ }
+ tarfilelist++;
+ }
+
+ r = send_request(fd, RQT_DL, RQT_DL_EXIT, NULL, 0);
+ if (r) {
+ fprintf(stderr, "\x1b[0;33;1mmissing RQT_DL_EXIT response "
+ "from broken bootloader\x1b[0m\n");
+ }
+
+ fprintf(stderr, "\nrequest target reboot : ");
+
+ r = request_reboot(fd);
+ if (r)
+ fprintf(stderr, "\x1b[0;31;1mfailed\x1b[0m\n");
+ else
+ fprintf(stderr, "\x1b[0;32;1msuccess\x1b[0m\n");
+
+ close(fd);
+
+ return 0;
+}
+
+int test_tar_entry(struct data_src *tardata)
+{
+ static unsigned char *chunk;
+ size_t n = 0, size;
+ int chunksize;
+
+ chunk = (unsigned char*)malloc(trans_unit_size);
+ if (chunk == NULL) {
+ fprintf(stderr, "Unable to allocate memory.\n");
+ return -1;
+ }
+
+ size = tardata->get_data_length(tardata);
+ while (n < size) {
+ if (size - n >= trans_unit_size)
+ chunksize = trans_unit_size;
+ else
+ chunksize = size - n;
+
+ chunksize = tardata->get_data_block(tardata, chunk, chunksize);
+ if (chunksize <= 0) {
+ free(chunk);
+ return -1;
+ }
+
+ if ((!chunksize > 0 && chunksize <= trans_unit_size)) {
+ free(chunk);
+ return -1;
+ }
+
+ if ((n/chunksize)%4 == 0)
+ fprintf(stderr, ".");
+
+ n += chunksize;
+ }
+
+ free(chunk);
+
+ return 0;
+}
+
+int test_tar_file(const char *tarfile)
+{
+ struct tar_data_src tardata;
+ const char *filename;
+ int r = 0;
+
+ fprintf(stderr, "tar %s\n", tarfile);
+
+ r = tardata_open(&tardata, tarfile);
+ if (r < 0) {
+ fprintf(stderr, "line %d: failed to open %s\n", __LINE__, tarfile);
+ return r;
+ }
+
+ while (1) {
+ filename = tardata_next(&tardata);
+ if (!filename)
+ break;
+
+ fprintf(stderr, " entry %s ", filename);
+
+ r = test_tar_entry(&tardata.src);
+ if (r) {
+ fprintf(stderr, "bad\n");
+ break;
+ }
+
+ fprintf(stderr, "\n");
+ }
+
+ tardata_close(&tardata);
+
+ return r;
+}
+
+
+int test_tar_file_list(char **tarfilelist)
+{
+ int r;
+
+ while (*tarfilelist) {
+ char *tarfile = *tarfilelist;
+
+ r = test_tar_file(tarfile);
+ if (r < 0)
+ fprintf(stderr, "failed to load %s\n", *tarfilelist);
+ tarfilelist++;
+ }
+
+ return 0;
+}
+
+void usage(const char *exename)
+{
+ fprintf(stderr, "%s: [-t] [-v] [-d port] [-p pitfile] [tar] [tar] ..\n",
+ exename);
+ exit(1);
+}
+
+int main(int argc, char **argv)
+{
+ const char *exename, *portname, *pitfile;
+ int opt;
+
+ exename = argv[0];
+
+ opt = 1;
+
+ pitfile = NULL;
+ portname = NULL;
+
+ printf("\n");
+ printf("Linux Thor downloader, version %s \n", PACKAGE_VERSION);
+ printf("Authors: YoungJin Lee <yj0701.lee@samsung.com>\n\n");
+
+ while (opt < argc) {
+ /* check if we're verbose */
+ if (!strcmp(argv[opt], "-v")) {
+ opt_verbose = 1;
+ opt++;
+ continue;
+ }
+
+ if (!strcmp(argv[opt], "-t")) {
+ opt_test = 1;
+ opt++;
+ continue;
+ }
+
+ if (!strcmp(argv[opt], "-p")) {
+ pitfile = argv[opt+1];
+ opt += 2;
+ continue;
+ }
+
+ if (!strcmp(argv[opt], "-d") && (opt+1) < argc) {
+ portname = argv[opt+1];
+ opt += 2;
+ continue;
+ }
+
+ break;
+ }
+
+ if (opt_test)
+ return test_tar_file_list(&argv[opt]);
+
+ if ((pitfile)&&(opt == argc))
+ return process_download(portname, pitfile, NULL);
+
+ if (opt < argc)
+ return process_download(portname, pitfile, &argv[opt]);
+
+ usage(exename);
+ return 0;
+}
+
diff --git a/thor-proto.h b/thor-proto.h
new file mode 100644
index 0000000..0b8ee39
--- /dev/null
+++ b/thor-proto.h
@@ -0,0 +1,78 @@
+#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. */
+};
+
+
+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. */
+};
+
+
+struct data_res_pkt {
+ int32_t ack; /* Ack. */
+ int32_t cnt; /* Int. Datas. */
+};
+
+
+#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)
+
+#endif /* __THOR_PROTO_H__ */