diff options
-rw-r--r-- | 99-lthor.rules | 3 | ||||
-rwxr-xr-x | CMakeLists.txt | 17 | ||||
-rw-r--r-- | lthor.c | 1384 | ||||
-rw-r--r-- | thor-proto.h | 78 |
4 files changed, 321 insertions, 1161 deletions
diff --git a/99-lthor.rules b/99-lthor.rules index dcd694d..47ee167 100644 --- a/99-lthor.rules +++ b/99-lthor.rules @@ -1,2 +1 @@ -SUBSYSTEM=="tty", KERNEL=="ttyACM*", MODE="0666" -SUBSYSTEM=="tty", KERNEL=="ttyACM*", ATTRS{idVendor}=="04e8", ATTRS{idProduct}=="685d", ENV{ID_MM_CANDIDATE}="null" +SUBSYSTEM=="usb", ATTRS{idVendor}=="04e8", ATTRS{idProduct}=="685d", MODE="0666" diff --git a/CMakeLists.txt b/CMakeLists.txt index fda3bcf..c667871 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,11 +1,19 @@ CMAKE_MINIMUM_REQUIRED(VERSION 2.6) PROJECT(lthor C) +SET(LIBTHOR_SRCS + libthor/thor_acm.c + libthor/thor.c + libthor/thor_raw_file.c + libthor/thor_tar.c + libthor/thor_usb.c +) + SET(SRCS lthor.c ) -INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/.) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/libthor) SET(VENDOR "samsung") SET(PACKAGE ${PROJECT_NAME}) @@ -25,6 +33,7 @@ MESSAGE("Build type: ${CMAKE_BUILD_TYPE}") INCLUDE(FindPkgConfig) pkg_check_modules(pkgs REQUIRED libarchive + libusb-1.0>=1.0.17 ) FOREACH(flag ${pkgs_CFLAGS}) @@ -45,12 +54,16 @@ ADD_DEFINITIONS("-DPACKAGE_VERSION=\"${PACKAGE_VERSION}\"") #SET(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed") IF(APPLE) SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}") + INCLUDE_DIRECTORIES("/usr/include/machine") ELSE() SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--as-needed") ENDIF() +ADD_LIBRARY(libthor ${LIBTHOR_SRCS}) + ADD_EXECUTABLE(${PROJECT_NAME} ${SRCS}) -TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${pkgs_LDFLAGS}) + +TARGET_LINK_LIBRARIES(${PROJECT_NAME} libthor ${pkgs_LDFLAGS}) INSTALL(TARGETS ${PROJECT_NAME} DESTINATION ${BINDIR}) @@ -15,1165 +15,306 @@ * limitations under the License. */ -#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 <stdio.h> +#include <getopt.h> #include <unistd.h> -#include <string.h> -#include <assert.h> -#include <sys/mman.h> -#include <poll.h> +#include <stdlib.h> #include <errno.h> -#include <dirent.h> -#include <libgen.h> -#include <getopt.h> - -#include <archive.h> -#include <archive_entry.h> +#include <string.h> +#include <stdint.h> +#include <sys/time.h> -#include "thor-proto.h" +#include "thor.h" #define KB (1024) #define MB (1024*KB) -#define DEFAULT_PKT_SIZE (1*MB) -#define DEFAULT_TIMEOUT (60000) /* 1 minute */ - -/* 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) { - if (opt_verbose) { - if (r > 0) - fprintf(stderr, "line %d: %d bytes requested, %d written\n", - __LINE__, trans_unit_size, r); - 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) { - if (opt_verbose) { - fprintf(stderr, "\x1b[0;31;1mMissmatch\x1b[0m\n"); - fprintf(stderr, "line %d: ack reports fail. ack = %d\n", - __LINE__, resp.ack); - } else { - fprintf(stderr, "\x1b[1A\x1b[16C\x1b[0;31;1mMissmatch\x1b[0m\n"); - } - return -2; - } - - 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); -} +struct helper { + struct thor_data_src *data; + enum thor_data_type type; + const char *name; +}; -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; -} +struct time_data { + struct timeval start_time; + struct timeval last_time; + int last_sent; +}; -int thor_handshake(int fd, int timeout, int log_on) +static int test_tar_file_list(char **tarfilelist) { - char buffer[4]; - int r; - - r = write(fd, "THOR", 4); - if (r != 4) { - if (log_on) - fprintf(stderr, "line %d: failed to write signature bytes\n", - __LINE__); - return -1; - } + struct thor_data_src *data; + int ret; - r = serial_read(fd, buffer, 4, timeout); - if (r != 4) { - if (log_on) - fprintf(stderr, "line %d: failed to read signature bytes\n", - __LINE__); - return -1; - } + while (*tarfilelist) { + ret = thor_get_data_src(*tarfilelist, THOR_FORMAT_TAR, &data); + if (ret) + goto error; - if (memcmp(buffer, "ROHT", 4)) { - if (log_on) - fprintf(stderr, "line %d: signature byte mismatch\n", - __LINE__); - return -1; + ret = thor_send_data(NULL, data, THOR_NORMAL_DATA, + NULL, NULL, NULL, NULL); + thor_release_data_src(data); + if (ret) + goto error; } 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); - +error: + fprintf(stderr, "Unable to load file %s: %d\n", *tarfilelist, ret); return ret; } -int find_usb_device(void) +/* Check if LHOR protocol is in working state */ +static int check_proto(struct thor_device_id *dev_id) { - DIR *usb_dir; - DIR *usb_dir_in; - char *p; - char buffer[11]; - const char *dirname = "/sys/bus/usb/devices"; - char usbpath[0x400]; - char usbdir[0x100]; - char *tty = NULL; - int fd, r; - - usb_dir = opendir(dirname); - if (!usb_dir) - return -1; - - while (1) { - struct dirent *de; - - de = readdir(usb_dir); - 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)]; - p[0] = 0x00; - - usb_dir_in = opendir(usbpath); - if (opt_verbose) - fprintf(stderr, "at %s\n", usbpath); - - while ((de = readdir(usb_dir_in))) { - 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) { - fd = get_termios(tty); - if (fd < 0) - continue; - - r = thor_handshake(fd, 2000, 0); - if (r < 0) { - close(fd); - continue; - } - - closedir(usb_dir_in); - return fd; - } - } - - closedir(usb_dir_in); - } - - closedir(usb_dir); + thor_device_handle *handle; + int ret; - if (opt_verbose) - fprintf(stderr, "No USB device found with matching\n"); + ret = thor_open(dev_id, 0, &handle); + if (ret) + fprintf(stderr, "Unable to open device: %d\n", ret); + else + thor_close(handle); - return -1; + return ret; } -int get_termios(const char *portname) +static int count_files(char **list) { - struct termios tios; - int fd, r; - - /* On OS X open serial port with O_NONBLOCK flag */ - fd = open(portname, O_RDWR | O_NONBLOCK); - - if (fd == -1) { - perror("port open error!!\n"); - return -1; - } - - /*now clear the O_NONBLOCK flag to enable writing big data chunks at once*/ - if (fcntl(fd, F_SETFL, 0)) { - printf("line %d: error clearing O_NONBLOCK\n", __LINE__); - return -1; - } + int i = 0; - r = tcgetattr(fd, &tios); - if (r < 0) { - fprintf(stderr, "line %d: tcgetattr failed\n", __LINE__); - close(fd); - 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__); - close(fd); - return -1; - } + while (list && list[i]) + ++i; - r = tcflush(fd, TCIOFLUSH); - if (r < 0) { - fprintf(stderr, "line %d: tcflush failed\n", __LINE__); - close(fd); - return -1; - } - - return fd; + return i; } -int open_port(const char *portname, int wait) +static int init_data_parts(const char *pitfile, char **tarfilelist, + struct helper *data_parts) { - int once = 0; - int fd, r; - - if (opt_test) - return open("/dev/null", O_RDWR); - - while (1) { - if (!portname) { - fd = find_usb_device(); - if (fd >= 0) - return fd; - - } else { - fd = get_termios(portname); - if (fd < 0) { - fprintf(stderr, "USB port is " - "\x1b[0;31;1mnot\x1b[0m detected !\n\n"); - return fd; - } - - r = thor_handshake(fd, DEFAULT_TIMEOUT, 1); - if (r < 0) { - close(fd); - return -1; - } - - return fd; - } - - if (!wait) { - fprintf(stderr, "line %d: device not found\n", __LINE__); - return -1; - } + int i; + int entry = 0; + int ret; - if (!once) { - if (!wait) - return -1; - 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; + if (pitfile) { + data_parts[0].type = THOR_PIT_DATA; + data_parts[0].name = pitfile; + ret = thor_get_data_src(pitfile, THOR_FORMAT_RAW, + &(data_parts[0].data)); + if (ret) { + fprintf(stderr, "Unable to open pit file %s : %d\n", + pitfile, ret); + return -EINVAL; } - sleep(1); + ++entry; } -} -int wait_and_open_port(const char *portname) -{ - return open_port(portname, 1); -} - -/* - * 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) { - if (opt_verbose) - fprintf(stderr, "RQT_DL_FILE_INFO, status = %08x\n", r); - return r; - } - 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; - } + while (*tarfilelist) { + printf("\x1b[0;33;1m%s :\x1b[0m\n", *tarfilelist); + data_parts[entry].type = THOR_NORMAL_DATA; + data_parts[0].name = *tarfilelist; + ret = thor_get_data_src(*tarfilelist, THOR_FORMAT_TAR, + &(data_parts[entry].data)); + if (ret) { + fprintf(stderr, "Unable to open file %s : %d\n", + *tarfilelist, ret); + goto err_cleanup_parts; + } - 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; + ++entry; + ++tarfilelist; } - 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); + return entry; +err_cleanup_parts: + for (i = 0; i < entry; ++i) + thor_release_data_src(data_parts[i].data); + return ret; } -size_t te_get_data_block(struct data_src *src, void *data, size_t len) +static void init_time_data(struct time_data *tdata) { - struct tar_data_src *tardata = (struct tar_data_src *) src; - - return archive_read_data(tardata->ar, data, len); + gettimeofday(&tdata->start_time, NULL); + memcpy(&tdata->last_time, &tdata->start_time, sizeof(tdata->start_time)); + tdata->last_sent = 0; } -const char *te_get_data_name(struct data_src *src) +static void report_next_entry(thor_device_handle *th, + struct thor_data_src *data, void *user_data) { - struct tar_data_src *tardata = (struct tar_data_src *) src; + struct time_data *tdata = user_data; - return archive_entry_pathname(tardata->ae); + printf("[\x1b[0;32;1m%s\x1b[0m]\n", data->get_name(data)); + init_time_data(tdata); } -int tardata_open(struct tar_data_src *tardata, const char *tarfile) +static double timediff(struct timeval *atv, struct timeval *btv) { - 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); + double diff; - if (r) { - fprintf(stderr, "line %d: failed to open %s\n", __LINE__, tarfile); - return r; + 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); } - 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; + return diff; } -const char *tardata_next(struct tar_data_src *tardata) +static void report_progress(thor_device_handle *th, struct thor_data_src *data, + int sent, int left, int chunk_nmb, void *user_data) { - 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; + struct time_data *tdata = user_data; + struct timeval *start_time = &tdata->start_time; + struct timeval *last_time = &tdata->last_time; + struct timeval current_time; + double diff; + int sent_kb = sent/KB; + int total_kb = (sent + left)/KB; + char progress [4] = { '-', '\\', '|', '/' }; + char c = progress[(sent_kb/30)%4]; + + fprintf(stderr, "\x1b[1A\x1b[16C%c sending %6dk/%6dk %3d%% block %-6d", + c, sent_kb, total_kb, ((sent_kb*100)/total_kb), chunk_nmb); + + gettimeofday(¤t_time, NULL); + + if (left != 0) { + diff = timediff(last_time, ¤t_time); + fprintf(stderr, " [%.2f MB/s]\n", + (double)((sent - tdata->last_sent)/diff)/(MB)); + tdata->last_sent = sent; + *last_time = current_time; + } else { + diff = timediff(start_time, ¤t_time); + fprintf(stderr, " [avg %.2f MB/s]\n", + (double)(sent/diff)/(MB)); } - - return archive_entry_pathname(tardata->ae); } -int tardata_close(struct tar_data_src *tardata) +static int do_download(thor_device_handle *th, struct helper *data_parts, + int entries, size_t total_size) { - archive_read_close(tardata->ar); - archive_read_finish(tardata->ar); - return 0; -} + struct time_data tdata; + int i; + int ret; -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; + ret = thor_start_session(th, total_size); + if (ret) { + fprintf(stderr, "Unable to start download session: %d\n", ret); + goto out; } - *total = 0; - - while (1) { - size_t size = 0; - - filename = tardata_next(&tardata); - if (!filename) + for (i = 0; i < entries; ++i) { + switch (data_parts[i].type) { + case THOR_PIT_DATA: + fprintf(stderr, "\nDownload PIT file : %s\n\n", + data_parts[i].name); 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); - filedata_close(&filedata); - 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) + case THOR_NORMAL_DATA: + fprintf(stderr, "\nDownload files from %s\n\n", + data_parts[i].name); break; - - printf("[\x1b[0;32;1m%s\x1b[0m]\n", filename); - r = download_single_file(fd, &tardata.src, BINARY_TYPE_NORMAL); - if (r == -1) { - fprintf(stderr, "line %d: failed to download %s\n", __LINE__, filename); - fprintf(stderr, "\nIn some cases, lthor needs enough memory\n"); - fprintf(stderr, "Please check free memory in your Host PC "); - fprintf(stderr, "and unload some heavy applications\n"); - tardata_close(&tardata); - return r; - } - } - - fprintf(stderr, "\n%s completed\n", tarfile); - - tardata_close(&tardata); - - return 0; -} - -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"); - close(fd); - 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__); - close(fd); - 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); - close(fd); - return -1; - } - if (pitfile) { - fprintf(stderr, "\nDownload PIT file : %s\n\n", pitfile); - r = download_pitfile(fd, pitfile); - if (r < 0) { - fprintf(stderr, "\nfailed to download %s\n", pitfile); - close(fd); - return r; + ret = thor_send_data(th, data_parts[i].data, data_parts[i].type, + report_progress, &tdata, report_next_entry, + &tdata); + if (ret) { + fprintf(stderr, "\nfailed to download %s: %d\n", + data_parts[i].name, ret); + goto out; } - } - while (tarfilelist && *tarfilelist) { - fprintf(stderr, "\nDownload files from %s\n\n", *tarfilelist); - r = download_single_tarfile(fd, *tarfilelist); - if (r < 0) { - fprintf(stderr, "\nfailed to download %s\n", *tarfilelist); - close(fd); - return r; - } - tarfilelist++; } - r = send_request(fd, RQT_DL, RQT_DL_EXIT, NULL, 0); - if (r) { + ret = thor_end_session(th); + if (ret) 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) + ret = thor_reboot(th); + if (ret) { fprintf(stderr, "\x1b[0;31;1mfailed\x1b[0m\n"); - else + goto out; + } else { fprintf(stderr, "\x1b[0;32;1msuccess\x1b[0m\n"); - - close(fd); - - return 0; + } +out: + return ret; } -/* Check if LHOR protocol is in working state */ -int check_proto(const char *portname) +static int process_download(struct thor_device_id *dev_id, const char *pitfile, + char **tarfilelist) { - int fd; - /* int r; - struct res_pkt resp;*/ - - /* connect to the target */ - fd = open_port(portname, 0); - if (fd < 0) - return -1; - - /* Below I commented out my attempt to check if LHOR protocol is enabled - * by quering protocol version. - * The main problem is that I didn't find a way to 'close' the session, - * to put protocol into previous state. - * */ - - /* - r = thor_handshake(fd); - if (r < 0) { - fprintf(stderr, "line %d: handshake failed\n", __LINE__); - return -1; - } - - r = send_request_timeout(fd, RQT_INFO, RQT_INFO_VER_PROTOCOL, NULL, 0, NULL, 0, &resp, DEFAULT_TIMEOUT); - if (r) { - fprintf(stderr, "RQT_INFO_VER_PROTOCOL, status = %08x\n", r); - return -1; - } */ - - /* Here should be some code, which closes protocol session, resets it somehow */ - - close(fd); - - return 0; -} + thor_device_handle *th; + size_t total_size = 0; + struct helper *data_parts; + int nfiles; + int entries = 0; + int i; + int ret; -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; + ret = thor_open(dev_id, 1, &th); + if (ret) { + fprintf(stderr, "Unable to open device: %d\n", ret); + return ret; } - 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 <= trans_unit_size && chunksize > 0)) { - free(chunk); - return -1; - } - - if ((n/chunksize)%4 == 0) - fprintf(stderr, "."); + nfiles = count_files(tarfilelist) + (pitfile ? 1 : 0); - n += chunksize; + data_parts = calloc(nfiles, sizeof(*data_parts)); + if (!data_parts) { + ret = -ENOMEM; + goto close_dev; } - 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; + entries = init_data_parts(pitfile, tarfilelist, data_parts); + if (entries < 0) { + ret = entries; + goto free_data_parts; } - while (1) { - filename = tardata_next(&tardata); - if (!filename) - break; - - fprintf(stderr, " entry %s ", filename); + /* Count the total size of data */ + for (i = 0; i < entries; ++i) { + size_t size = data_parts[i].data->get_size(data_parts[i].data); - r = test_tar_entry(&tardata.src); - if (r) { - fprintf(stderr, "bad\n"); + switch (data_parts[i].type) { + case THOR_PIT_DATA: + printf("\x1b[0;33;1m%s : \x1b[0m%zuk\n", + data_parts[i].name, size/KB); + break; + case THOR_NORMAL_DATA: + default: break; } - - fprintf(stderr, "\n"); + total_size += size; } - tardata_close(&tardata); + printf("-------------------------\n"); + printf("\t\x1b[0;33;1mtotal\x1b[0m :\t%.2fMB\n\n", + (double)total_size/MB); - return r; -} + ret = do_download(th, data_parts, entries, total_size); + for (i = 0; i < entries; ++i) + thor_release_data_src(data_parts[i].data); -int test_tar_file_list(char **tarfilelist) -{ - int r; +free_data_parts: + free(data_parts); +close_dev: + thor_close(th); - while (*tarfilelist) { - char *tarfile = *tarfilelist; - - r = test_tar_file(tarfile); - if (r < 0) - fprintf(stderr, "failed to load %s\n", *tarfilelist); - tarfilelist++; - } - - return 0; + return ret; } -void usage(const char *exename) +static void usage(const char *exename) { fprintf(stderr, "Usage: %s: [options] [-d port] [-p pitfile] [tar] [tar] ..\n" @@ -1181,26 +322,55 @@ void usage(const char *exename) " -t, --test Don't flash, just check if given tar files are correct\n" " -v, --verbose Be more verbose\n" " -c, --check Don't flash, just check if given tty port is thor capable\n" - " -d=<port>, --port=<port> Use specific tty port for communication\n" " -p=<pitfile>, --pitfile=<pitfile> Flash new partition table\n" + " -b=<busid>, --busid=<busid> Flash device with given busid\n" + " --vendor-id=<vid> Flash device with given Vendor ID\n" + " --product-id=<pid> Flash device with given Product ID\n" + " --serial=<serialno> Flash device with given Serial Number\n" " --help Print this help message\n", exename); exit(1); } +static void d_opt_obsolete() +{ + fprintf(stderr, + "--port, -p options are obsolete.\n" + "Instead you may use:" + " -b=<busid>, --busid=<busid> Flash device with given busid\n" + " --vendor-id=<vid> Flash device with given Vendor ID\n" + " --product-id=<pid> Flash device with given Product ID\n" + " --serial=<serialno> Flash device with given Serial Number\n"); + exit(1); +} + int main(int argc, char **argv) { - const char *exename = NULL, *portname = NULL, *pitfile = NULL; + const char *exename = NULL, *pitfile = NULL; int opt; + int opt_test = 0; int opt_check = 0; + int opt_verbose = 0; /* unused for now */ int optindex; + int ret; + struct thor_device_id dev_id = { + .busid = NULL, + .vid = -1, + .pid = -1, + .serial = NULL, + }; + struct option opts[] = { {"test", no_argument, 0, 't'}, {"verbose", no_argument, 0, 'v'}, {"check", no_argument, 0, 'c'}, {"port", required_argument, 0, 'd'}, {"pitfile", required_argument, 0, 'p'}, - {"help", no_argument, 0, 1}, + {"busid", required_argument, 0, 'b'}, + {"vendor-id", required_argument, 0, 1}, + {"product-id", required_argument, 0, 2}, + {"serial", required_argument, 0, 3}, + {"help", no_argument, 0, 0}, {0, 0, 0, 0} }; @@ -1211,8 +381,14 @@ int main(int argc, char **argv) exename = argv[0]; + ret = thor_init(); + if (ret) { + fprintf(stderr, "Unable to init io backend: %d\n", ret); + exit(-1); + } + while (1) { - opt = getopt_long(argc, argv, "tvcd:p:", opts, &optindex); + opt = getopt_long(argc, argv, "tvcd:p:b:", opts, &optindex); if (opt == -1) break; @@ -1227,31 +403,81 @@ int main(int argc, char **argv) opt_check = 1; break; case 'd': - portname = optarg; + d_opt_obsolete(); break; case 'p': pitfile = optarg; break; + case 'b': + dev_id.busid = optarg; + break; case 1: + { + unsigned long int val; + char *endptr = NULL; + + val = strtoul(optarg, &endptr, 0); + if (*optarg == '\0' + || (endptr && *endptr != '\0')) { + fprintf(stderr, + "Invalid value type for --vendor-id option.\n" + "Expected a number but got: %s", optarg); + exit(-1); + } + + if (val > UINT16_MAX) { + fprintf(stderr, + "Value of --vendor-id out of range\n"); + exit(-1); + } + + dev_id.vid = (int)val; + break; + } + case 2: + { + unsigned long int val; + char *endptr = NULL; + + val = strtoul(optarg, &endptr, 0); + if (*optarg == '\0' + || (endptr && *endptr != '\0')) { + fprintf(stderr, + "Invalid value type for --product-id option.\n" + "Expected a number but got: %s", optarg); + exit(-1); + } + + if (val > UINT16_MAX) { + fprintf(stderr, + "Value of --product-id out of range\n"); + exit(-1); + } + + dev_id.vid = (int)val; + break; + } + case 3: + dev_id.serial = optarg; + break; + case 0: default: usage(exename); return 0; } } + ret = 0; if (opt_test) - return test_tar_file_list(&(argv[optind])); - - if (opt_check) - return check_proto(portname); - - if (pitfile && !argv[optind]) - return process_download(portname, pitfile, NULL); - - if (argv[optind]) - return process_download(portname, pitfile, &(argv[optind])); + ret = test_tar_file_list(&(argv[optind])); + else if (opt_check) + ret = check_proto(&dev_id); + else if (pitfile || argv[optind]) + ret = process_download(&dev_id, pitfile, &(argv[optind])); + else + usage(exename); - usage(exename); - return 0; + thor_cleanup(); + return ret; } diff --git a/thor-proto.h b/thor-proto.h deleted file mode 100644 index 0b8ee39..0000000 --- a/thor-proto.h +++ /dev/null @@ -1,78 +0,0 @@ -#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__ */ |