diff options
author | YoungJin Lee <yj0701.lee@samsung.com> | 2012-11-02 19:55:18 +0900 |
---|---|---|
committer | YoungJin Lee <yj0701.lee@samsung.com> | 2012-11-02 19:55:18 +0900 |
commit | 4b15e5f2e0f5d910dad4e71956de0fa0122b4929 (patch) | |
tree | 6f4516b39eede691d95fe86fe28491bd75e70931 | |
parent | 9e3ce8bc980d2eed0c544c4520d75df9adb268ac (diff) | |
download | lthor-4b15e5f2e0f5d910dad4e71956de0fa0122b4929.tar.gz lthor-4b15e5f2e0f5d910dad4e71956de0fa0122b4929.tar.bz2 lthor-4b15e5f2e0f5d910dad4e71956de0fa0122b4929.zip |
initial commit of lthor
Change-Id: Ifd62a81919f2a88a22b38627dd467d53c5408823
-rwxr-xr-x | CMakeLists.txt | 52 | ||||
-rwxr-xr-x | lthor.c | 1162 | ||||
-rw-r--r-- | thor-proto.h | 78 |
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}) @@ -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(<v, &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(<v, &etv); + memcpy(<v, &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__ */ |