X-Git-Url: https://review.openocd.org/gitweb?p=openocd.git;a=blobdiff_plain;f=src%2Fjtag%2Fdrivers%2Fopendous.c;h=ae21cf2b9b498e9f9e4fb39f663e72d507170828;hp=c973bd233045f186510008e5045d4dde6f1b30e7;hb=1d1386e31ed47dcdd9b433b810b27a0f28928bc7;hpb=0eaa8e7db06416e3781eca58211df1280308a6c6 diff --git a/src/jtag/drivers/opendous.c b/src/jtag/drivers/opendous.c index c973bd2330..ae21cf2b9b 100644 --- a/src/jtag/drivers/opendous.c +++ b/src/jtag/drivers/opendous.c @@ -23,9 +23,7 @@ * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + * along with this program. If not, see . * ***************************************************************************/ #ifdef HAVE_CONFIG_H @@ -34,44 +32,63 @@ #include #include -#include "libusb_common.h" +#include "libusb_helper.h" #include -#include #include -#define ESTICK_VID 0x1781 -#define ESTICK_PID 0xC0C0 - -#define OPENDOUS_VID 0x03EB -#define OPENDOUS_PID 0x204F +#define OPENDOUS_MAX_VIDS_PIDS 4 +/* define some probes with similar interface */ +struct opendous_probe { + const char *name; + uint16_t VID[OPENDOUS_MAX_VIDS_PIDS]; + uint16_t PID[OPENDOUS_MAX_VIDS_PIDS]; + uint8_t READ_EP; + uint8_t WRITE_EP; + uint8_t CONTROL_TRANSFER; + int BUFFERSIZE; +}; -/* pid could be specified at runtime */ -static uint16_t vids[] = { OPENDOUS_VID, ESTICK_VID, 0 }; -static uint16_t pids[] = { OPENDOUS_PID, ESTICK_PID, 0 }; +static const struct opendous_probe opendous_probes[] = { + {"usbprog-jtag", {0x1781, 0}, {0x0C63, 0}, 0x82, 0x02, 0x00, 510 }, + {"opendous", {0x1781, 0x03EB, 0}, {0xC0C0, 0x204F, 0}, 0x81, 0x02, 0x00, 360 }, + {"usbvlab", {0x16C0, 0}, {0x05DC, 0}, 0x81, 0x02, 0x01, 360 }, + {NULL, {0x0000}, {0x0000}, 0x00, 0x00, 0x00, 0 } +}; -#define OPENDOUS_WRITE_ENDPOINT 0x02 -#define OPENDOUS_READ_ENDPOINT 0x81 +#define OPENDOUS_WRITE_ENDPOINT (opendous_probe->WRITE_EP) +#define OPENDOUS_READ_ENDPOINT (opendous_probe->READ_EP) static unsigned int opendous_hw_jtag_version = 1; #define OPENDOUS_USB_TIMEOUT 1000 -#define OPENDOUS_USB_BUFFER_SIZE 360 +#define OPENDOUS_USB_BUFFER_SIZE (opendous_probe->BUFFERSIZE) #define OPENDOUS_IN_BUFFER_SIZE (OPENDOUS_USB_BUFFER_SIZE) #define OPENDOUS_OUT_BUFFER_SIZE (OPENDOUS_USB_BUFFER_SIZE) /* Global USB buffers */ -static uint8_t usb_in_buffer[OPENDOUS_IN_BUFFER_SIZE]; -static uint8_t usb_out_buffer[OPENDOUS_OUT_BUFFER_SIZE]; +static uint8_t *usb_in_buffer; +static uint8_t *usb_out_buffer; /* Constants for OPENDOUS command */ #define OPENDOUS_MAX_SPEED 66 -#define OPENDOUS_MAX_TAP_TRANSMIT 350 /* even number is easier to handle */ +#define OPENDOUS_MAX_TAP_TRANSMIT ((opendous_probe->BUFFERSIZE)-10) #define OPENDOUS_MAX_INPUT_DATA (OPENDOUS_MAX_TAP_TRANSMIT*4) +/* TAP */ #define OPENDOUS_TAP_BUFFER_SIZE 65536 +struct pending_scan_result { + int first; /* First bit position in tdo_buffer to read */ + int length; /* Number of bits to read */ + struct scan_command *command; /* Corresponding scan command */ + uint8_t *buffer; +}; + +static int pending_scan_results_length; +static struct pending_scan_result *pending_scan_results_buffer; + #define MAX_PENDING_SCAN_RESULTS (OPENDOUS_MAX_INPUT_DATA) /* JTAG usb commands */ @@ -84,11 +101,16 @@ static uint8_t usb_out_buffer[OPENDOUS_OUT_BUFFER_SIZE]; #define JTAG_CMD_SET_SRST_TRST 0x6 #define JTAG_CMD_READ_CONFIG 0x7 +/* usbvlab control transfer */ +#define FUNC_START_BOOTLOADER 30 +#define FUNC_WRITE_DATA 0x50 +#define FUNC_READ_DATA 0x51 + +static char *opendous_type; +static const struct opendous_probe *opendous_probe; + /* External interface functions */ static int opendous_execute_queue(void); -static int opendous_speed(int speed); -static int opendous_speed_div(int speed, int *khz); -static int opendous_khz(int khz, int *jtag_speed); static int opendous_init(void); static int opendous_quit(void); @@ -112,7 +134,7 @@ static void opendous_tap_append_scan(int length, uint8_t *buffer, struct scan_co /* opendous lowlevel functions */ struct opendous_jtag { - struct jtag_libusb_device_handle *usb_handle; + struct libusb_device_handle *usb_handle; }; static struct opendous_jtag *opendous_usb_open(void); @@ -122,12 +144,10 @@ static int opendous_usb_write(struct opendous_jtag *opendous_jtag, int out_lengt static int opendous_usb_read(struct opendous_jtag *opendous_jtag); /* helper functions */ -int opendous_get_version_info(void); +static int opendous_get_version_info(void); #ifdef _DEBUG_USB_COMMS_ -char time_str[50]; static void opendous_debug_buffer(uint8_t *buffer, int length); -char *opendous_get_time(char *); #endif static struct opendous_jtag *opendous_jtag_handle; @@ -135,6 +155,22 @@ static struct opendous_jtag *opendous_jtag_handle; /***************************************************************************/ /* External interface implementation */ +COMMAND_HANDLER(opendous_handle_opendous_type_command) +{ + if (CMD_ARGC == 0) + return ERROR_OK; + + /* only if the cable name wasn't overwritten by cmdline */ + if (!opendous_type) { + /* REVISIT first verify that it's listed in cables[] ... */ + opendous_type = strdup(CMD_ARGV[0]); + } + + /* REVISIT it's probably worth returning the current value ... */ + + return ERROR_OK; +} + COMMAND_HANDLER(opendous_handle_opendous_info_command) { if (opendous_get_version_info() == ERROR_OK) { @@ -149,7 +185,7 @@ COMMAND_HANDLER(opendous_handle_opendous_hw_jtag_command) { switch (CMD_ARGC) { case 0: - command_print(CMD_CTX, "opendous hw jtag %i", opendous_hw_jtag_version); + command_print(CMD, "opendous hw jtag %i", opendous_hw_jtag_version); break; case 1: { @@ -179,6 +215,7 @@ static const struct command_registration opendous_command_handlers[] = { .handler = &opendous_handle_opendous_info_command, .mode = COMMAND_EXEC, .help = "show opendous info", + .usage = "", }, { .name = "opendous_hw_jtag", @@ -187,18 +224,29 @@ static const struct command_registration opendous_command_handlers[] = { .help = "access opendous HW JTAG command version", .usage = "[2|3]", }, + { + .name = "opendous_type", + .handler = &opendous_handle_opendous_type_command, + .mode = COMMAND_CONFIG, + .help = "set opendous type", + .usage = "[usbvlab|usbprog-jtag|opendous]", + }, COMMAND_REGISTRATION_DONE }; -struct jtag_interface opendous_interface = { +static struct jtag_interface opendous_interface = { + .execute_queue = opendous_execute_queue, +}; + +struct adapter_driver opendous_adapter_driver = { .name = "opendous", + .transports = jtag_only, .commands = opendous_command_handlers, - .execute_queue = opendous_execute_queue, - .speed = opendous_speed, - .speed_div = opendous_speed_div, - .khz = opendous_khz, + .init = opendous_init, .quit = opendous_quit, + + .jtag_ops = &opendous_interface, }; static int opendous_execute_queue(void) @@ -208,10 +256,10 @@ static int opendous_execute_queue(void) enum scan_type type; uint8_t *buffer; - while (cmd != NULL) { + while (cmd) { switch (cmd->type) { case JTAG_RUNTEST: - DEBUG_JTAG_IO("runtest %i cycles, end in %i", cmd->cmd.runtest->num_cycles, \ + LOG_DEBUG_IO("runtest %i cycles, end in %i", cmd->cmd.runtest->num_cycles, cmd->cmd.runtest->end_state); if (cmd->cmd.runtest->end_state != -1) @@ -220,7 +268,7 @@ static int opendous_execute_queue(void) break; case JTAG_TLR_RESET: - DEBUG_JTAG_IO("statemove end in %i", cmd->cmd.statemove->end_state); + LOG_DEBUG_IO("statemove end in %i", cmd->cmd.statemove->end_state); if (cmd->cmd.statemove->end_state != -1) opendous_end_state(cmd->cmd.statemove->end_state); @@ -228,21 +276,21 @@ static int opendous_execute_queue(void) break; case JTAG_PATHMOVE: - DEBUG_JTAG_IO("pathmove: %i states, end in %i", \ - cmd->cmd.pathmove->num_states, \ + LOG_DEBUG_IO("pathmove: %i states, end in %i", + cmd->cmd.pathmove->num_states, cmd->cmd.pathmove->path[cmd->cmd.pathmove->num_states - 1]); opendous_path_move(cmd->cmd.pathmove->num_states, cmd->cmd.pathmove->path); break; case JTAG_SCAN: - DEBUG_JTAG_IO("scan end in %i", cmd->cmd.scan->end_state); + LOG_DEBUG_IO("scan end in %i", cmd->cmd.scan->end_state); if (cmd->cmd.scan->end_state != -1) opendous_end_state(cmd->cmd.scan->end_state); scan_size = jtag_build_buffer(cmd->cmd.scan, &buffer); - DEBUG_JTAG_IO("scan input, length = %d", scan_size); + LOG_DEBUG_IO("scan input, length = %d", scan_size); #ifdef _DEBUG_USB_COMMS_ opendous_debug_buffer(buffer, (scan_size + 7) / 8); @@ -252,7 +300,7 @@ static int opendous_execute_queue(void) break; case JTAG_RESET: - DEBUG_JTAG_IO("reset trst: %i srst %i", cmd->cmd.reset->trst, cmd->cmd.reset->srst); + LOG_DEBUG_IO("reset trst: %i srst %i", cmd->cmd.reset->trst, cmd->cmd.reset->srst); opendous_tap_execute(); @@ -262,7 +310,7 @@ static int opendous_execute_queue(void) break; case JTAG_SLEEP: - DEBUG_JTAG_IO("sleep %i", cmd->cmd.sleep->us); + LOG_DEBUG_IO("sleep %" PRIu32, cmd->cmd.sleep->us); opendous_tap_execute(); jtag_sleep(cmd->cmd.sleep->us); break; @@ -276,36 +324,37 @@ static int opendous_execute_queue(void) return opendous_tap_execute(); } -/* Sets speed in kHz. */ -static int opendous_speed(int speed) +static int opendous_init(void) { - if (speed <= OPENDOUS_MAX_SPEED) { - /* one day... */ - return ERROR_OK; - } else - LOG_INFO("Requested speed %dkHz exceeds maximum of %dkHz, ignored", speed, OPENDOUS_MAX_SPEED); + int check_cnt; + const struct opendous_probe *cur_opendous_probe; - return ERROR_OK; -} + cur_opendous_probe = opendous_probes; -static int opendous_speed_div(int speed, int *khz) -{ - *khz = speed; + if (!opendous_type) { + opendous_type = strdup("opendous"); + LOG_WARNING("No opendous_type specified, using default 'opendous'"); + } - return ERROR_OK; -} + while (cur_opendous_probe->name) { + if (strcmp(cur_opendous_probe->name, opendous_type) == 0) { + opendous_probe = cur_opendous_probe; + break; + } + cur_opendous_probe++; + } -static int opendous_khz(int khz, int *jtag_speed) -{ - *jtag_speed = khz; - /* TODO: convert this into delay value for opendous */ + if (!opendous_probe) { + LOG_ERROR("No matching cable found for %s", opendous_type); + return ERROR_JTAG_INIT_FAILED; + } - return ERROR_OK; -} -static int opendous_init(void) -{ - int check_cnt; + usb_in_buffer = malloc(opendous_probe->BUFFERSIZE); + usb_out_buffer = malloc(opendous_probe->BUFFERSIZE); + + pending_scan_results_buffer = malloc( + MAX_PENDING_SCAN_RESULTS * sizeof(*pending_scan_results_buffer)); opendous_jtag_handle = opendous_usb_open(); @@ -336,6 +385,19 @@ static int opendous_init(void) static int opendous_quit(void) { opendous_usb_close(opendous_jtag_handle); + + free(usb_out_buffer); + usb_out_buffer = NULL; + + free(usb_in_buffer); + usb_in_buffer = NULL; + + free(pending_scan_results_buffer); + pending_scan_results_buffer = NULL; + + free(opendous_type); + opendous_type = NULL; + return ERROR_OK; } @@ -422,7 +484,9 @@ void opendous_scan(int ir_scan, enum scan_type type, uint8_t *buffer, int scan_s /* Move to appropriate scan state */ opendous_end_state(ir_scan ? TAP_IRSHIFT : TAP_DRSHIFT); - opendous_state_move(); + if (tap_get_state() != tap_get_end_state()) + opendous_state_move(); + opendous_end_state(saved_end_state); /* Scan */ @@ -463,9 +527,10 @@ void opendous_simple_command(uint8_t command, uint8_t _data) { int result; - DEBUG_JTAG_IO("0x%02x 0x%02x", command, _data); + LOG_DEBUG_IO("0x%02x 0x%02x", command, _data); - usb_out_buffer[0] = (uint16_t) 2; + usb_out_buffer[0] = 2; + usb_out_buffer[1] = 0; usb_out_buffer[2] = command; usb_out_buffer[3] = _data; @@ -479,7 +544,7 @@ int opendous_get_status(void) return ERROR_OK; } -int opendous_get_version_info(void) +static int opendous_get_version_info(void) { return ERROR_OK; } @@ -491,16 +556,6 @@ static int tap_length; static uint8_t tms_buffer[OPENDOUS_TAP_BUFFER_SIZE]; static uint8_t tdo_buffer[OPENDOUS_TAP_BUFFER_SIZE]; -struct pending_scan_result { - int first; /* First bit position in tdo_buffer to read */ - int length; /* Number of bits to read */ - struct scan_command *command; /* Corresponding scan command */ - uint8_t *buffer; -}; - -static int pending_scan_results_length; -static struct pending_scan_result pending_scan_results_buffer[MAX_PENDING_SCAN_RESULTS]; - static int last_tms; void opendous_tap_init(void) @@ -512,8 +567,9 @@ void opendous_tap_init(void) void opendous_tap_ensure_space(int scans, int bits) { int available_scans = MAX_PENDING_SCAN_RESULTS - pending_scan_results_length; + int available_bits = OPENDOUS_TAP_BUFFER_SIZE / 2 - tap_length; - if (scans > available_scans) + if ((scans > available_scans) || (bits > available_bits)) opendous_tap_execute(); } @@ -523,6 +579,8 @@ void opendous_tap_append_step(int tms, int tdi) unsigned char _tms = tms ? 1 : 0; unsigned char _tdi = tdi ? 1 : 0; + opendous_tap_ensure_space(0, 1); + int tap_index = tap_length / 4; int bits = (tap_length % 4) * 2; @@ -530,7 +588,7 @@ void opendous_tap_append_step(int tms, int tdi) if (!bits) tms_buffer[tap_index] = 0; - tms_buffer[tap_index] |= (_tdi << bits)|(_tms << (bits + 1)) ; + tms_buffer[tap_index] |= (_tdi << bits)|(_tms << (bits + 1)); tap_length++; } else LOG_ERROR("opendous_tap_append_step, overflow"); @@ -538,7 +596,7 @@ void opendous_tap_append_step(int tms, int tdi) void opendous_tap_append_scan(int length, uint8_t *buffer, struct scan_command *command) { - DEBUG_JTAG_IO("append scan, length = %d", length); + LOG_DEBUG_IO("append scan, length = %d", length); struct pending_scan_result *pending_scan_result = &pending_scan_results_buffer[pending_scan_results_length]; int i; @@ -557,54 +615,55 @@ void opendous_tap_append_scan(int length, uint8_t *buffer, struct scan_command * * For the purpose of padding we assume that we are in idle or pause state. */ int opendous_tap_execute(void) { - int byte_length, byte_length_out; + int byte_length; int i, j; int result; - int output_counter; + +#ifdef _DEBUG_USB_COMMS_ + int byte_length_out; +#endif if (tap_length > 0) { /* memset(tdo_buffer,0,OPENDOUS_TAP_BUFFER_SIZE); */ /* LOG_INFO("OPENDOUS tap execute %d",tap_length); */ byte_length = (tap_length + 3) / 4; - byte_length_out = (tap_length + 7) / 8; #ifdef _DEBUG_USB_COMMS_ + byte_length_out = (tap_length + 7) / 8; LOG_DEBUG("opendous is sending %d bytes", byte_length); #endif - output_counter = 0; for (j = 0, i = 0; j < byte_length;) { - int recieve; + int receive; int transmit = byte_length - j; if (transmit > OPENDOUS_MAX_TAP_TRANSMIT) { transmit = OPENDOUS_MAX_TAP_TRANSMIT; - recieve = (OPENDOUS_MAX_TAP_TRANSMIT) / 2; + receive = (OPENDOUS_MAX_TAP_TRANSMIT) / 2; usb_out_buffer[2] = JTAG_CMD_TAP_OUTPUT; } else { usb_out_buffer[2] = JTAG_CMD_TAP_OUTPUT | ((tap_length % 4) << 4); - recieve = (transmit + 1) / 2; + receive = (transmit + 1) / 2; } usb_out_buffer[0] = (transmit + 1) & 0xff; usb_out_buffer[1] = ((transmit + 1) >> 8) & 0xff; memmove(usb_out_buffer + 3, tms_buffer + j, transmit); - result = opendous_usb_message(opendous_jtag_handle, 3 + transmit, recieve); - if (result != recieve) { - LOG_ERROR("opendous_tap_execute, wrong result %d, expected %d", result, recieve); + result = opendous_usb_message(opendous_jtag_handle, 3 + transmit, receive); + if (result != receive) { + LOG_ERROR("opendous_tap_execute, wrong result %d, expected %d", result, receive); return ERROR_JTAG_QUEUE_FAILED; } - memmove(tdo_buffer + i, usb_in_buffer, recieve); - i += recieve; + memmove(tdo_buffer + i, usb_in_buffer, receive); + i += receive; j += transmit; } - result = byte_length_out; #ifdef _DEBUG_USB_COMMS_ - LOG_DEBUG("opendous tap result %d", result); - opendous_debug_buffer(tdo_buffer, result); + LOG_DEBUG("opendous tap result %d", byte_length_out); + opendous_debug_buffer(tdo_buffer, byte_length_out); #endif /* LOG_INFO("eStick tap execute %d",tap_length); */ @@ -619,7 +678,7 @@ int opendous_tap_execute(void) /* Copy to buffer */ buf_set_buf(tdo_buffer, first, buffer, 0, length); - DEBUG_JTAG_IO("pending scan result, length = %d", length); + LOG_DEBUG_IO("pending scan result, length = %d", length); #ifdef _DEBUG_USB_COMMS_ opendous_debug_buffer(buffer, byte_length_out); @@ -630,8 +689,7 @@ int opendous_tap_execute(void) return ERROR_JTAG_QUEUE_FAILED; } - if (pending_scan_result->buffer != NULL) - free(pending_scan_result->buffer); + free(pending_scan_result->buffer); } opendous_tap_init(); @@ -647,14 +705,14 @@ struct opendous_jtag *opendous_usb_open(void) { struct opendous_jtag *result; - struct jtag_libusb_device_handle *devh; - if (jtag_libusb_open(vids, pids, &devh) != ERROR_OK) + struct libusb_device_handle *devh; + if (jtag_libusb_open(opendous_probe->VID, opendous_probe->PID, &devh, NULL) != ERROR_OK) return NULL; jtag_libusb_set_configuration(devh, 0); - jtag_libusb_claim_interface(devh, 0); + libusb_claim_interface(devh, 0); - result = (struct opendous_jtag *) malloc(sizeof(struct opendous_jtag)); + result = malloc(sizeof(*result)); result->usb_handle = devh; return result; } @@ -696,15 +754,21 @@ int opendous_usb_write(struct opendous_jtag *opendous_jtag, int out_length) } #ifdef _DEBUG_USB_COMMS_ - LOG_DEBUG("%s: USB write begin", opendous_get_time(time_str)); + LOG_DEBUG("USB write begin"); #endif - result = jtag_libusb_bulk_write(opendous_jtag->usb_handle, OPENDOUS_WRITE_ENDPOINT, \ - (char *)usb_out_buffer, out_length, OPENDOUS_USB_TIMEOUT); + if (opendous_probe->CONTROL_TRANSFER) { + result = jtag_libusb_control_transfer(opendous_jtag->usb_handle, + LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT, + FUNC_WRITE_DATA, 0, 0, (char *) usb_out_buffer, out_length, OPENDOUS_USB_TIMEOUT); + } else { + jtag_libusb_bulk_write(opendous_jtag->usb_handle, OPENDOUS_WRITE_ENDPOINT, + (char *)usb_out_buffer, out_length, OPENDOUS_USB_TIMEOUT, &result); + } #ifdef _DEBUG_USB_COMMS_ - LOG_DEBUG("%s: USB write end: %d bytes", opendous_get_time(time_str), result); + LOG_DEBUG("USB write end: %d bytes", result); #endif - DEBUG_JTAG_IO("opendous_usb_write, out_length = %d, result = %d", out_length, result); + LOG_DEBUG_IO("opendous_usb_write, out_length = %d, result = %d", out_length, result); #ifdef _DEBUG_USB_COMMS_ opendous_debug_buffer(usb_out_buffer, out_length); @@ -716,14 +780,21 @@ int opendous_usb_write(struct opendous_jtag *opendous_jtag, int out_length) int opendous_usb_read(struct opendous_jtag *opendous_jtag) { #ifdef _DEBUG_USB_COMMS_ - LOG_DEBUG("%s: USB read begin", opendous_get_time(time_str)); + LOG_DEBUG("USB read begin"); #endif - int result = jtag_libusb_bulk_read(opendous_jtag->usb_handle, OPENDOUS_READ_ENDPOINT, - (char *)usb_in_buffer, OPENDOUS_IN_BUFFER_SIZE, OPENDOUS_USB_TIMEOUT); + int result; + if (opendous_probe->CONTROL_TRANSFER) { + result = jtag_libusb_control_transfer(opendous_jtag->usb_handle, + LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_IN, + FUNC_READ_DATA, 0, 0, (char *) usb_in_buffer, OPENDOUS_IN_BUFFER_SIZE, OPENDOUS_USB_TIMEOUT); + } else { + jtag_libusb_bulk_read(opendous_jtag->usb_handle, OPENDOUS_READ_ENDPOINT, + (char *)usb_in_buffer, OPENDOUS_IN_BUFFER_SIZE, OPENDOUS_USB_TIMEOUT, &result); + } #ifdef _DEBUG_USB_COMMS_ - LOG_DEBUG("%s: USB read end: %d bytes", opendous_get_time(time_str), result); + LOG_DEBUG("USB read end: %d bytes", result); #endif - DEBUG_JTAG_IO("opendous_usb_read, result = %d", result); + LOG_DEBUG_IO("opendous_usb_read, result = %d", result); #ifdef _DEBUG_USB_COMMS_ opendous_debug_buffer(usb_in_buffer, result); @@ -750,15 +821,4 @@ void opendous_debug_buffer(uint8_t *buffer, int length) LOG_DEBUG("%s", line); } } - -char *opendous_get_time(char *str) -{ - struct timeb timebuffer; - char *timeline; - - ftime(&timebuffer); - timeline = ctime(&(timebuffer.time)); - snprintf(str, 49, "%.8s.%hu", &timeline[11], timebuffer.millitm); - return str; -} #endif