Created
August 17, 2018 16:56
-
-
Save milosgajdos/dbb11a9b2e0cd70805f385234ee645eb to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright 2017 Intel Corporation. | |
* The source code, information and material ("Material") contained herein is | |
* owned by Intel Corporation or its suppliers or licensors, and title to such | |
* Material remains with Intel Corporation or its suppliers or licensors. | |
* The Material contains proprietary information of Intel or its suppliers and | |
* licensors. The Material is protected by worldwide copyright laws and treaty | |
* provisions. | |
* No part of the Material may be used, copied, reproduced, modified, published, | |
* uploaded, posted, transmitted, distributed or disclosed in any way without | |
* Intel's prior express written permission. No license under any patent, | |
* copyright or other intellectual property rights in the Material is granted to | |
* or conferred upon you, either expressly, by implication, inducement, estoppel | |
* or otherwise. | |
* Any license under such intellectual property rights must be express and | |
* approved by Intel in writing. | |
*/ | |
#define _GNU_SOURCE | |
#include <dlfcn.h> // For dladdr | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <unistd.h> | |
#include <fcntl.h> | |
#include <sys/types.h> | |
#include <dirent.h> | |
#include <time.h> | |
#include <pthread.h> | |
#include "mvnc.h" | |
#include "usb_link.h" | |
#include "usb_boot.h" | |
#include "common.h" | |
#ifdef __APPLE__ | |
#include <mach/clock.h> | |
#include <mach/mach.h> | |
#endif | |
// Graph file structure | |
#define HEADER_LENGTH 264 | |
#define STAGE_LENGTH 227 | |
#define VERSION_OFFSET 36 | |
#define GRAPH_VERSION 2 | |
#define N_STAGES_OFFSET 240 | |
#define FIRST_SHAVE_OFFSET 248 | |
#define N_OUTPUTS_OFFSET (HEADER_LENGTH + 136) | |
#define X_OUT_STRIDE_OFFSET (HEADER_LENGTH + 172) | |
#define THERMAL_BUFFER_SIZE 100 | |
#define DEBUG_BUFFER_SIZE 120 | |
#define MAX_OPTIMISATIONS 40 | |
#define OPTIMISATION_NAME_LEN 50 | |
#define OPTIMISATION_LIST_BUFFER_SIZE (MAX_OPTIMISATIONS * OPTIMISATION_NAME_LEN) | |
#define MAX_PATH_LENGTH 255 | |
#define STATUS_WAIT_TIMEOUT 15 | |
static int initialized = 0; | |
static pthread_mutex_t mm = PTHREAD_MUTEX_INITIALIZER; | |
int mvnc_loglevel = 0; | |
/////////////////////////// Structs ///////////////////////////// | |
struct Graph; | |
struct Device { | |
int backoff_time_normal, backoff_time_high, backoff_time_critical; | |
int temperature_debug, throttle_happened; | |
float temp_lim_upper, temp_lim_lower; | |
float *thermal_stats; | |
char *dev_addr; // Device USB address as returned by usb_ | |
char *dev_file; // Device filename in /dev directory | |
char *optimisation_list; | |
void *usb_link; | |
struct Device *next; // Next device in chain | |
struct Graph *graphs; // List of associated graphs | |
pthread_mutex_t mm; | |
} *devices; | |
struct Graph { | |
int started; | |
int have_data; | |
int dont_block; | |
int input_idx; | |
int output_idx; | |
int failed; | |
int iterations; | |
int network_throttle; | |
unsigned noutputs; | |
unsigned nstages; | |
struct Device *dev; | |
struct Graph *next; | |
char *aux_buffer; | |
char *debug_buffer; | |
float *time_taken; | |
void *user_param[2]; | |
void *output_data; | |
}; | |
static double time_in_seconds() | |
{ | |
static double s; | |
struct timespec ts; | |
#ifdef __APPLE__ // OS X does not have clock_gettime, use clock_get_time | |
clock_serv_t cclock; | |
mach_timespec_t mts; | |
host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); | |
clock_get_time(cclock, &mts); | |
mach_port_deallocate(mach_task_self(), cclock); | |
ts.tv_sec = mts.tv_sec; | |
ts.tv_nsec = mts.tv_nsec; | |
#else | |
clock_gettime(CLOCK_REALTIME, &ts); | |
#endif | |
if (!s) | |
s = ts.tv_sec + ts.tv_nsec * 1e-9; | |
return ts.tv_sec + ts.tv_nsec * 1e-9 - s; | |
} | |
static void initialize() | |
{ | |
// We sanitize the situation by trying to reset the devices that have been left open | |
initialized = 1; | |
usblink_resetall(); | |
} | |
mvncStatus mvncGetDeviceName(int index, char *name, unsigned int nameSize) | |
{ | |
if (index < 0 || !name || nameSize < MVNC_MAX_NAME_SIZE) | |
return MVNC_INVALID_PARAMETERS; | |
pthread_mutex_lock(&mm); | |
if (!initialized) | |
initialize(); | |
int rc = usb_find_device(index, name, nameSize, 0, 0, 0); | |
pthread_mutex_unlock(&mm); | |
return rc; | |
} | |
static int is_device_opened(const char *name) | |
{ | |
struct Device *d = devices; | |
while (d) { | |
if (strcmp(d->dev_addr, name) == 0) | |
return 0; | |
d = d->next; | |
} | |
return -1; | |
} | |
static mvncStatus load_fw_file(const char *name) | |
{ | |
int rc; | |
FILE *fp; | |
char *tx_buf; | |
unsigned file_size; | |
char mv_cmd_file[MAX_PATH_LENGTH], *p; | |
// Search the mvnc executable in the same directory of this library, under mvnc | |
Dl_info info; | |
dladdr(mvncOpenDevice, &info); | |
strncpy(mv_cmd_file, info.dli_fname, sizeof(mv_cmd_file) - 40); | |
p = strrchr(mv_cmd_file, '/'); | |
if (p) | |
strcpy(p + 1, "mvnc/MvNCAPI.mvcmd"); | |
else | |
strcpy(mv_cmd_file, "mvnc/MvNCAPI.mvcmd"); | |
// Load the mvnc executable | |
fp = fopen(mv_cmd_file, "rb"); | |
if (fp == NULL) { | |
if (mvnc_loglevel) | |
perror(mv_cmd_file); | |
pthread_mutex_unlock(&mm); | |
return MVNC_MVCMD_NOT_FOUND; | |
} | |
fseek(fp, 0, SEEK_END); | |
file_size = ftell(fp); | |
rewind(fp); | |
if (!(tx_buf = malloc(file_size))) { | |
if (mvnc_loglevel) | |
perror("buffer"); | |
fclose(fp); | |
pthread_mutex_unlock(&mm); | |
return MVNC_OUT_OF_MEMORY; | |
} | |
if (fread(tx_buf, 1, file_size, fp) != file_size) { | |
if (mvnc_loglevel) | |
perror(mv_cmd_file); | |
fclose(fp); | |
free(tx_buf); | |
pthread_mutex_unlock(&mm); | |
return MVNC_MVCMD_NOT_FOUND; | |
} | |
fclose(fp); | |
// Boot it | |
rc = usb_boot(name, tx_buf, file_size); | |
free(tx_buf); | |
if (rc) { | |
pthread_mutex_unlock(&mm); | |
return rc; | |
} | |
PRINT_DEBUG(stderr, "Boot successful, device address %s\n", name); | |
return MVNC_OK; | |
} | |
static void allocate_device(const char* name, void **deviceHandle, void* f) | |
{ | |
struct Device *d = calloc(1, sizeof(*d)); | |
d->dev_addr = strdup(name); | |
d->usb_link = f; | |
d->next = devices; | |
d->temp_lim_upper = 95; | |
d->temp_lim_lower = 85; | |
d->backoff_time_normal = 0; | |
d->backoff_time_high = 100; | |
d->backoff_time_critical = 10000; | |
d->temperature_debug = 0; | |
pthread_mutex_init(&d->mm, 0); | |
devices = d; | |
*deviceHandle = d; | |
PRINT_DEBUG(stderr, "done\n"); | |
PRINT_INFO(stderr, "Booted %s -> %s\n", | |
d->dev_addr, | |
d->dev_file ? d->dev_file : "VSC"); | |
} | |
mvncStatus mvncOpenDevice(const char *name, void **deviceHandle) | |
{ | |
int rc; | |
char name2[MVNC_MAX_NAME_SIZE] = ""; | |
char* device_name; | |
char* saved_name = NULL; | |
char* temp = NULL; //save to be able to free memory | |
int second_name_available = 0; | |
if (!name || !deviceHandle) | |
return MVNC_INVALID_PARAMETERS; | |
temp = saved_name = strdup(name); | |
device_name = strtok_r(saved_name, ":", &saved_name); | |
if (device_name == NULL) { | |
free(temp); | |
return MVNC_INVALID_PARAMETERS; | |
} | |
pthread_mutex_lock(&mm); | |
if (!initialized) | |
initialize(); | |
rc = load_fw_file(device_name); | |
if (rc != MVNC_OK) { | |
free(temp); | |
return rc; | |
} | |
if (saved_name && strlen(saved_name) > 0) { | |
device_name = strtok_r(NULL, ":", &saved_name); | |
second_name_available = 1; | |
} | |
// Now we should have a new /dev/ttyACM, try to open it | |
double waittm = time_in_seconds() + STATUS_WAIT_TIMEOUT; | |
while (time_in_seconds() < waittm) { | |
void *f = usblink_open(device_name); | |
//we might fail in case name changed after boot and we don't have it | |
if (f == NULL && !second_name_available) { | |
int count = 0; | |
while (1) { | |
name2[0] = '\0'; | |
rc = usb_find_device(count, name2, | |
sizeof(name2), NULL, | |
DEFAULT_OPEN_VID, | |
DEFAULT_OPEN_PID); | |
if (rc < 0) //Error or no more devices found | |
break; | |
//check if we already have name2 open | |
// if not, check if it's not already busy | |
if (is_device_opened(name2) < 0 && | |
(f = usblink_open(name2))) | |
break; | |
count++; | |
} | |
} | |
if (f) { | |
myriadStatus_t status; | |
if (!usblink_getmyriadstatus(f, &status) && status == MYRIAD_WAITING) { | |
allocate_device(strlen(name2) > 0 ? name2 : device_name, deviceHandle, f); | |
free(temp); | |
pthread_mutex_unlock(&mm); | |
return MVNC_OK; | |
} else { | |
PRINT_DEBUG(stderr, | |
"found, but cannot get status\n"); | |
usblink_close(f); | |
} | |
} | |
// Error opening it, continue searching | |
usleep(10000); | |
} | |
free(temp); | |
pthread_mutex_unlock(&mm); | |
return MVNC_ERROR; | |
} | |
static int find_device(void *deviceHandle) | |
{ | |
struct Device *d = devices; | |
while (d) { | |
if (d == deviceHandle) | |
return 0; | |
d = d->next; | |
} | |
return -1; | |
} | |
static int find_graph(void *graphHandle) | |
{ | |
struct Device *d = devices; | |
while (d) { | |
struct Graph *g = d->graphs; | |
while (g) { | |
if (g == graphHandle) | |
return 0; | |
g = g->next; | |
} | |
d = d->next; | |
} | |
return -1; | |
} | |
// Defined here as it will be used twice | |
static int deallocate_graph(struct Graph *g) | |
{ | |
int found = 0; | |
// Remove it from the list of the associated device | |
if (g->dev->graphs == g) { | |
g->dev->graphs = g->next; | |
found = 1; | |
} else { | |
struct Graph *gp = g->dev->graphs; | |
while (gp->next) { | |
if (gp->next == g) { | |
found = 1; | |
gp->next = gp->next->next; | |
break; | |
} | |
gp = gp->next; | |
} | |
} | |
// Free it with all its data | |
if (found) { | |
free(g->aux_buffer); | |
free(g->output_data); | |
g->dev->thermal_stats = 0; | |
free(g); | |
} | |
return -!found; | |
} | |
mvncStatus mvncCloseDevice(void *deviceHandle) | |
{ | |
int found = 0; | |
if (!deviceHandle) | |
return MVNC_INVALID_PARAMETERS; | |
pthread_mutex_lock(&mm); | |
if (find_device(deviceHandle)) { | |
pthread_mutex_unlock(&mm); | |
return MVNC_INVALID_PARAMETERS; | |
} | |
struct Device *d = (struct Device *) deviceHandle; | |
// Remove it from our list | |
if (devices == d) { | |
devices = d->next; | |
found = 1; | |
} else { | |
struct Device *dp = devices; | |
while (dp->next) { | |
if (dp->next == d) { | |
found = 1; | |
dp->next = dp->next->next; | |
break; | |
} | |
dp = dp->next; | |
} | |
} | |
if (!found) { | |
pthread_mutex_unlock(&mm); | |
return MVNC_INVALID_PARAMETERS; | |
} | |
// Deallocate all associated graphs | |
pthread_mutex_lock(&d->mm); | |
while (d->graphs) | |
deallocate_graph(d->graphs); | |
// Reset | |
usblink_resetmyriad(d->usb_link); | |
usblink_close(d->usb_link); | |
if (d->optimisation_list) | |
free(d->optimisation_list); | |
free(d->dev_addr); | |
free(d->dev_file); | |
pthread_mutex_unlock(&d->mm); | |
pthread_mutex_destroy(&d->mm); | |
free(d); | |
pthread_mutex_unlock(&mm); | |
usleep(500000); | |
return MVNC_OK; | |
} | |
static unsigned read_32bits(const unsigned char *ptr) | |
{ | |
return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24); | |
} | |
mvncStatus mvncAllocateGraph(void *deviceHandle, void **graphHandle, | |
const void *graphFile, unsigned int graphFileLength) | |
{ | |
if (!deviceHandle || !graphHandle || !graphFile) | |
return MVNC_INVALID_PARAMETERS; | |
if (graphFileLength < HEADER_LENGTH + STAGE_LENGTH || | |
graphFileLength > 512 * 1024 * 1024) | |
return MVNC_UNSUPPORTED_GRAPH_FILE; | |
unsigned char *graph = (unsigned char *) graphFile; | |
if (graph[VERSION_OFFSET] != GRAPH_VERSION) | |
return MVNC_UNSUPPORTED_GRAPH_FILE; | |
unsigned nstages = graph[N_STAGES_OFFSET] + (graph[N_STAGES_OFFSET + 1] << 8); | |
unsigned noutputs = read_32bits(graph + N_OUTPUTS_OFFSET + | |
(nstages - 1) * STAGE_LENGTH) * | |
read_32bits(graph + N_OUTPUTS_OFFSET + | |
(nstages - 1) * STAGE_LENGTH + 4) * | |
read_32bits(graph + X_OUT_STRIDE_OFFSET + | |
(nstages - 1) * STAGE_LENGTH) / 2; | |
// A reasonable check on graph correctness | |
if (noutputs > 64 * 1024 * 1024) | |
return MVNC_UNSUPPORTED_GRAPH_FILE; | |
pthread_mutex_lock(&mm); | |
struct Device *d = devices; | |
while (d) { | |
if (d == deviceHandle) | |
break; | |
d = d->next; | |
} | |
if (!d) { | |
pthread_mutex_unlock(&mm); | |
return MVNC_INVALID_PARAMETERS; | |
} | |
if (d->graphs) { | |
pthread_mutex_unlock(&mm); | |
return MVNC_BUSY; | |
} | |
myriadStatus_t status; | |
double timeout = time_in_seconds() + 10; | |
do { | |
if (usblink_getmyriadstatus(d->usb_link, &status)) { | |
pthread_mutex_unlock(&mm); | |
return MVNC_ERROR; | |
} | |
usleep(10000); | |
} while (status != MYRIAD_WAITING && time_in_seconds() < timeout); | |
if (status != MYRIAD_WAITING) { | |
pthread_mutex_unlock(&mm); | |
return MVNC_ERROR; | |
} | |
if (usblink_setdata(d->usb_link, "blobFile", graphFile, graphFileLength, 0)) { | |
pthread_mutex_unlock(&mm); | |
return MVNC_ERROR; | |
} | |
struct Graph *g = calloc(1, sizeof(*g)); | |
g->dev = d; | |
g->nstages = nstages; | |
g->noutputs = noutputs; | |
// aux_buffer | |
g->aux_buffer = calloc(1, 224 + nstages * sizeof(*g->time_taken)); | |
if (!g->aux_buffer) { | |
free(g); | |
pthread_mutex_unlock(&mm); | |
return MVNC_OUT_OF_MEMORY; | |
} | |
if (usblink_setdata(g->dev->usb_link, "auxBuffer", g->aux_buffer, | |
224 + nstages * sizeof(*g->time_taken), 0)) { | |
free(g->aux_buffer); | |
free(g); | |
pthread_mutex_unlock(&mm); | |
return MVNC_ERROR; | |
} | |
g->debug_buffer = g->aux_buffer; | |
g->time_taken = (float *) (g->aux_buffer + 224); | |
// output_data | |
g->output_data = calloc(noutputs, 2); | |
if (!g->output_data) { | |
free(g->aux_buffer); | |
free(g); | |
pthread_mutex_unlock(&mm); | |
return MVNC_OUT_OF_MEMORY; | |
} | |
g->dev->thermal_stats = (float *) (g->aux_buffer + DEBUG_BUFFER_SIZE); | |
g->iterations = 1; | |
g->network_throttle = 1; | |
if (d->graphs) | |
g->next = d->graphs; | |
d->graphs = g; | |
*graphHandle = g; | |
pthread_mutex_unlock(&mm); | |
return MVNC_OK; | |
} | |
mvncStatus mvncDeallocateGraph(void *graphHandle) | |
{ | |
if (!graphHandle) | |
return MVNC_INVALID_PARAMETERS; | |
pthread_mutex_lock(&mm); | |
if (find_graph(graphHandle)) { | |
pthread_mutex_unlock(&mm); | |
return MVNC_INVALID_PARAMETERS; | |
} | |
struct Device *d = ((struct Graph *) graphHandle)->dev; | |
pthread_mutex_lock(&d->mm); | |
if (deallocate_graph((struct Graph *) graphHandle)) { | |
pthread_mutex_unlock(&d->mm); | |
pthread_mutex_unlock(&mm); | |
return MVNC_INVALID_PARAMETERS; | |
} | |
pthread_mutex_unlock(&d->mm); | |
pthread_mutex_unlock(&mm); | |
return MVNC_OK; | |
} | |
mvncStatus mvncSetGraphOption(void *graphHandle, int option, const void *data, | |
unsigned int dataLength) | |
{ | |
if (!graphHandle || !data || dataLength != 4) | |
return MVNC_INVALID_PARAMETERS; | |
struct Graph *g = (struct Graph *) graphHandle; | |
pthread_mutex_lock(&mm); | |
if (find_graph(graphHandle)) { | |
pthread_mutex_unlock(&mm); | |
return MVNC_INVALID_PARAMETERS; | |
} | |
pthread_mutex_lock(&g->dev->mm); | |
pthread_mutex_unlock(&mm); | |
switch (option) { | |
case MVNC_ITERATIONS: | |
g->iterations = *(int *) data; | |
break; | |
case MVNC_NETWORK_THROTTLE: | |
g->network_throttle = *(int *) data; | |
break; | |
case MVNC_DONT_BLOCK: | |
g->dont_block = *(int *) data; | |
break; | |
default: | |
pthread_mutex_unlock(&g->dev->mm); | |
return MVNC_INVALID_PARAMETERS; | |
} | |
pthread_mutex_unlock(&g->dev->mm); | |
return MVNC_OK; | |
} | |
mvncStatus mvncGetGraphOption(void *graphHandle, int option, void *data, | |
unsigned int *dataLength) | |
{ | |
if (!graphHandle || !data || !dataLength) | |
return MVNC_INVALID_PARAMETERS; | |
struct Graph *g = (struct Graph *) graphHandle; | |
pthread_mutex_lock(&mm); | |
if (find_graph(graphHandle)) { | |
pthread_mutex_unlock(&mm); | |
return MVNC_INVALID_PARAMETERS; | |
} | |
pthread_mutex_lock(&g->dev->mm); | |
pthread_mutex_unlock(&mm); | |
switch (option) { | |
case MVNC_ITERATIONS: | |
*(int *) data = g->iterations; | |
*dataLength = sizeof(int); | |
break; | |
case MVNC_NETWORK_THROTTLE: | |
*(int *) data = g->network_throttle; | |
*dataLength = sizeof(int); | |
break; | |
case MVNC_DONT_BLOCK: | |
*(int *) data = g->dont_block; | |
*dataLength = sizeof(int); | |
break; | |
case MVNC_TIME_TAKEN: | |
*(float **) data = g->time_taken; | |
*dataLength = sizeof(*g->time_taken) * g->nstages; | |
break; | |
case MVNC_DEBUG_INFO: | |
*(char **) data = g->debug_buffer; | |
*dataLength = DEBUG_BUFFER_SIZE; | |
break; | |
default: | |
pthread_mutex_unlock(&g->dev->mm); | |
return MVNC_INVALID_PARAMETERS; | |
} | |
pthread_mutex_unlock(&g->dev->mm); | |
return MVNC_OK; | |
} | |
mvncStatus mvncSetGlobalOption(int option, const void *data, | |
unsigned int dataLength) | |
{ | |
if (!data || dataLength != 4) | |
return MVNC_INVALID_PARAMETERS; | |
switch (option) { | |
case MVNC_LOG_LEVEL: | |
mvnc_loglevel = *(int *) data; | |
break; | |
default: | |
return MVNC_INVALID_PARAMETERS; | |
} | |
return MVNC_OK; | |
} | |
mvncStatus mvncGetGlobalOption(int option, void *data, unsigned int *dataLength) | |
{ | |
if (!data || !dataLength) | |
return MVNC_INVALID_PARAMETERS; | |
switch (option) { | |
case MVNC_LOG_LEVEL: | |
*(int *) data = mvnc_loglevel; | |
*dataLength = sizeof(mvnc_loglevel); | |
break; | |
default: | |
return MVNC_INVALID_PARAMETERS; | |
} | |
return MVNC_OK; | |
} | |
mvncStatus mvncSetDeviceOption(void *deviceHandle, int option, const void *data, | |
unsigned int dataLength) | |
{ | |
if (deviceHandle == 0 && option == MVNC_LOG_LEVEL) { | |
PRINT("Warning: MVNC_LOG_LEVEL is not a Device Option, \ | |
please use mvncSetGlobalOption()!\n"); | |
return mvncSetGlobalOption(option, data, dataLength); | |
} | |
if (!deviceHandle || !data || dataLength != 4) | |
return MVNC_INVALID_PARAMETERS; | |
struct Device *d = (struct Device *) deviceHandle; | |
pthread_mutex_lock(&mm); | |
if (find_device(d)) { | |
pthread_mutex_unlock(&mm); | |
return MVNC_INVALID_PARAMETERS; | |
} | |
pthread_mutex_lock(&d->mm); | |
pthread_mutex_unlock(&mm); | |
switch (option) { | |
case MVNC_TEMP_LIM_LOWER: | |
d->temp_lim_lower = *(float *) data; | |
break; | |
case MVNC_TEMP_LIM_HIGHER: | |
d->temp_lim_upper = *(float *) data; | |
break; | |
case MVNC_BACKOFF_TIME_NORMAL: | |
d->backoff_time_normal = *(int *) data; | |
break; | |
case MVNC_BACKOFF_TIME_HIGH: | |
d->backoff_time_high = *(int *) data; | |
break; | |
case MVNC_BACKOFF_TIME_CRITICAL: | |
d->backoff_time_critical = *(int *) data; | |
break; | |
case MVNC_TEMPERATURE_DEBUG: | |
d->temperature_debug = *(int *) data; | |
break; | |
default: | |
pthread_mutex_unlock(&d->mm); | |
return MVNC_INVALID_PARAMETERS; | |
} | |
pthread_mutex_unlock(&d->mm); | |
return MVNC_OK; | |
} | |
static mvncStatus get_optimisation_list(struct Device *d) | |
{ | |
int i, config[10]; | |
double timeout; | |
myriadStatus_t status; | |
char *p; | |
if (d->optimisation_list) | |
return MVNC_OK; | |
d->optimisation_list = calloc(OPTIMISATION_LIST_BUFFER_SIZE, 1); | |
if (!d->optimisation_list) | |
return MVNC_OUT_OF_MEMORY; | |
memset(config, 0, sizeof(config)); | |
config[0] = 1; | |
config[1] = 1; | |
if (usblink_setdata(d->usb_link, "config", config, sizeof(config), 1)) | |
return MVNC_ERROR; | |
timeout = time_in_seconds() + STATUS_WAIT_TIMEOUT; | |
do { | |
if (usblink_getmyriadstatus(d->usb_link, &status)) | |
return MVNC_ERROR; | |
usleep(10000); | |
} while (status != MYRIAD_WAITING && | |
status != MYRIAD_FINISHED && time_in_seconds() < timeout); | |
if (status != MYRIAD_WAITING && status != MYRIAD_FINISHED) | |
return MVNC_TIMEOUT; | |
if (usblink_getdata(d->usb_link, "optimizationList", | |
d->optimisation_list, OPTIMISATION_LIST_BUFFER_SIZE, 0, 0)) | |
return MVNC_ERROR; | |
for (i = 0; i < MAX_OPTIMISATIONS; i++) { | |
p = strchr(d->optimisation_list + i * OPTIMISATION_NAME_LEN, '~'); | |
if (p) | |
*p = 0; | |
} | |
config[1] = 0; | |
if (usblink_setdata(d->usb_link, "config", config, sizeof(config), 0)) | |
return MVNC_ERROR; | |
return MVNC_OK; | |
} | |
mvncStatus mvncGetDeviceOption(void *deviceHandle, int option, void *data, | |
unsigned int *dataLength) | |
{ | |
mvncStatus rc; | |
if (deviceHandle == 0 && option == MVNC_LOG_LEVEL) { | |
PRINT("Warning: MVNC_LOG_LEVEL is not a Device Option, \ | |
please use mvncGetGlobalOption()!\n"); | |
return mvncGetGlobalOption(option, data, dataLength); | |
} | |
if (!deviceHandle || !data || !dataLength) | |
return MVNC_INVALID_PARAMETERS; | |
struct Device *d = (struct Device *) deviceHandle; | |
pthread_mutex_lock(&mm); | |
if (find_device(d)) { | |
pthread_mutex_unlock(&mm); | |
return MVNC_INVALID_PARAMETERS; | |
} | |
pthread_mutex_lock(&d->mm); | |
pthread_mutex_unlock(&mm); | |
switch (option) { | |
case MVNC_TEMP_LIM_LOWER: | |
*(float *) data = d->temp_lim_lower; | |
*dataLength = sizeof(int); | |
break; | |
case MVNC_TEMP_LIM_HIGHER: | |
*(float *) data = d->temp_lim_upper; | |
*dataLength = sizeof(int); | |
break; | |
case MVNC_BACKOFF_TIME_NORMAL: | |
*(int *) data = d->backoff_time_normal; | |
*dataLength = sizeof(int); | |
break; | |
case MVNC_BACKOFF_TIME_HIGH: | |
*(int *) data = d->backoff_time_high; | |
*dataLength = sizeof(int); | |
break; | |
case MVNC_BACKOFF_TIME_CRITICAL: | |
*(int *) data = d->backoff_time_critical; | |
*dataLength = sizeof(int); | |
break; | |
case MVNC_TEMPERATURE_DEBUG: | |
*(int *) data = d->temperature_debug; | |
*dataLength = sizeof(int); | |
break; | |
case MVNC_THERMAL_STATS: | |
if (!d->thermal_stats) { | |
pthread_mutex_unlock(&d->mm); | |
return MVNC_NO_DATA; | |
} | |
*(float **) data = d->thermal_stats; | |
*dataLength = THERMAL_BUFFER_SIZE; | |
break; | |
case MVNC_OPTIMISATION_LIST: | |
rc = get_optimisation_list(d); | |
if (rc) { | |
pthread_mutex_unlock(&d->mm); | |
return rc; | |
} | |
*(char **) data = d->optimisation_list; | |
*dataLength = OPTIMISATION_LIST_BUFFER_SIZE; | |
break; | |
case MVNC_THERMAL_THROTTLING_LEVEL: | |
*(int *) data = d->throttle_happened; | |
*dataLength = sizeof(int); | |
break; | |
default: | |
pthread_mutex_unlock(&d->mm); | |
return MVNC_INVALID_PARAMETERS; | |
} | |
pthread_mutex_unlock(&d->mm); | |
return MVNC_OK; | |
} | |
static int send_opt_data(struct Graph *g) | |
{ | |
int config[10]; | |
config[0] = 1; // Version | |
config[1] = 0; // Query disable | |
config[2] = g->iterations; | |
config[3] = g->dev->temp_lim_upper; | |
config[4] = g->dev->temp_lim_lower; | |
config[5] = g->dev->backoff_time_normal; | |
config[6] = g->dev->backoff_time_high; | |
config[7] = g->dev->backoff_time_critical; | |
config[8] = g->dev->temperature_debug; | |
config[9] = g->network_throttle; | |
if (usblink_setdata(g->dev->usb_link, "config", config, sizeof(config), 0)) | |
return MVNC_ERROR; | |
return MVNC_OK; | |
} | |
mvncStatus mvncLoadTensor(void *graphHandle, const void *inputTensor, | |
unsigned int inputTensorLength, void *userParam) | |
{ | |
if (!graphHandle || !inputTensor || inputTensorLength < 2) | |
return MVNC_INVALID_PARAMETERS; | |
struct Graph *g = (struct Graph *) graphHandle; | |
pthread_mutex_lock(&mm); | |
if (find_graph(graphHandle)) { | |
pthread_mutex_unlock(&mm); | |
return MVNC_INVALID_PARAMETERS; | |
} | |
if (!g->started) { | |
if (send_opt_data(g)) { | |
pthread_mutex_unlock(&mm); | |
return MVNC_ERROR; | |
} | |
g->started = 1; | |
} | |
while (g->have_data == 2) { | |
if (g->dont_block) { | |
pthread_mutex_unlock(&mm); | |
return MVNC_BUSY; | |
} | |
if (g->failed) { | |
pthread_mutex_unlock(&mm); | |
return MVNC_ERROR; | |
} | |
pthread_mutex_unlock(&mm); | |
usleep(1000); | |
pthread_mutex_lock(&mm); | |
if (find_graph(g)) { | |
pthread_mutex_unlock(&mm); | |
return MVNC_GONE; | |
} | |
} | |
pthread_mutex_lock(&g->dev->mm); | |
pthread_mutex_unlock(&mm); | |
if (usblink_setdata(g->dev->usb_link, g->input_idx ? "input2" : "input1", | |
inputTensor, inputTensorLength, g->have_data == 0)) { | |
pthread_mutex_unlock(&mm); | |
return MVNC_ERROR; | |
} | |
g->user_param[g->input_idx] = userParam; | |
g->input_idx = !g->input_idx; | |
g->have_data++; | |
pthread_mutex_unlock(&g->dev->mm); | |
return MVNC_OK; | |
} | |
mvncStatus mvncGetResult(void *graphHandle, void **outputData, | |
unsigned int *outputDataLength, void **userParam) | |
{ | |
int rc, unlock_own = 0; | |
if (!graphHandle || !outputData || !outputDataLength) | |
return MVNC_INVALID_PARAMETERS; | |
struct Graph *g = (struct Graph *) graphHandle; | |
pthread_mutex_lock(&mm); | |
if (find_graph(graphHandle)) { | |
pthread_mutex_unlock(&mm); | |
return MVNC_INVALID_PARAMETERS; | |
} | |
while (!g->have_data) { | |
if (g->dont_block) { | |
pthread_mutex_unlock(&mm); | |
return MVNC_NO_DATA; | |
} | |
pthread_mutex_unlock(&mm); | |
usleep(1000); | |
pthread_mutex_lock(&mm); | |
if (find_graph(g)) { | |
pthread_mutex_unlock(&mm); | |
return MVNC_GONE; | |
} | |
} | |
double timeout = time_in_seconds() + STATUS_WAIT_TIMEOUT; | |
do { | |
pthread_mutex_lock(&g->dev->mm); | |
pthread_mutex_unlock(&mm); | |
if (!usblink_getdata(g->dev->usb_link, "output", g->output_data, | |
2 * g->noutputs, 0, 0)) { | |
unsigned int length = DEBUG_BUFFER_SIZE + THERMAL_BUFFER_SIZE + | |
sizeof(int) + sizeof(*g->time_taken) * g->nstages; | |
if (usblink_getdata(g->dev->usb_link, "auxBuffer", g->aux_buffer, | |
length, 0, g->have_data == 2)) { | |
g->failed = 1; | |
pthread_mutex_unlock(&g->dev->mm); | |
return MVNC_ERROR; | |
} | |
unlock_own = 1; | |
break; | |
} | |
pthread_mutex_unlock(&g->dev->mm); | |
usleep(1000); | |
pthread_mutex_lock(&mm); | |
if (find_graph(g)) { | |
pthread_mutex_unlock(&mm); | |
return MVNC_GONE; | |
} | |
} while (time_in_seconds() < timeout); | |
g->dev->throttle_happened = *(int *) (g->aux_buffer + DEBUG_BUFFER_SIZE | |
+ THERMAL_BUFFER_SIZE); | |
*outputData = g->output_data; | |
*outputDataLength = 2 * g->noutputs; | |
*userParam = g->user_param[g->output_idx]; | |
g->output_idx = !g->output_idx; | |
g->have_data--; | |
if (unlock_own) { | |
rc = *g->debug_buffer ? MVNC_MYRIAD_ERROR : MVNC_OK; | |
if (rc) | |
g->failed = 1; | |
pthread_mutex_unlock(&g->dev->mm); | |
} else { | |
rc = MVNC_TIMEOUT; | |
g->failed = 1; | |
pthread_mutex_unlock(&mm); | |
} | |
return rc; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright 2017 Intel Corporation. | |
* The source code, information and material ("Material") contained herein is | |
* owned by Intel Corporation or its suppliers or licensors, and title to such | |
* Material remains with Intel Corporation or its suppliers or licensors. | |
* The Material contains proprietary information of Intel or its suppliers and | |
* licensors. The Material is protected by worldwide copyright laws and treaty | |
* provisions. | |
* No part of the Material may be used, copied, reproduced, modified, published, | |
* uploaded, posted, transmitted, distributed or disclosed in any way without | |
* Intel's prior express written permission. No license under any patent, | |
* copyright or other intellectual property rights in the Material is granted to | |
* or conferred upon you, either expressly, by implication, inducement, estoppel | |
* or otherwise. | |
* Any license under such intellectual property rights must be express and | |
* approved by Intel in writing. | |
*/ | |
// USB utility for use with Myriad2v2 ROM | |
// Very heavily modified from Sabre version of usb_boot | |
// Author: David Steinberg <[email protected]> | |
#include <unistd.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <stdint.h> | |
#include <time.h> | |
#include <string.h> | |
#include <sys/stat.h> | |
#include <getopt.h> | |
#include <errno.h> | |
#include <ctype.h> | |
#include <libusb.h> | |
#include "usb_boot.h" | |
#include "mvnc.h" | |
#include "common.h" | |
#ifdef __APPLE__ | |
#include <mach/clock.h> | |
#include <mach/mach.h> | |
#endif | |
#define DEFAULT_WRITE_TIMEOUT 2000 | |
#define DEFAULT_CONNECT_TIMEOUT 20 // in 100ms units | |
#define DEFAULT_CHUNK_SZ 1024 * 1024 | |
static unsigned int bulk_chunk_len = DEFAULT_CHUNK_SZ; | |
static int write_timeout = DEFAULT_WRITE_TIMEOUT; | |
static int connect_timeout = DEFAULT_CONNECT_TIMEOUT; | |
static int initialized; | |
void __attribute__ ((constructor)) usb_library_load() | |
{ | |
initialized = !libusb_init(NULL); | |
} | |
void __attribute__ ((destructor)) usb_library_unload() | |
{ | |
if (initialized) | |
libusb_exit(NULL); | |
} | |
typedef struct timespec highres_time_t; | |
static inline void highres_gettime(highres_time_t *ptr) | |
{ | |
#ifdef __APPLE__ // OS X does not have clock_gettime, use clock_get_time | |
clock_serv_t cclock; | |
mach_timespec_t mts; | |
host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); | |
clock_get_time(cclock, &mts); | |
mach_port_deallocate(mach_task_self(), cclock); | |
ptr->tv_sec = mts.tv_sec; | |
ptr->tv_nsec = mts.tv_nsec; | |
#else | |
clock_gettime(CLOCK_REALTIME, ptr); | |
#endif | |
} | |
static inline double highres_elapsed_ms(highres_time_t *start, highres_time_t *end) | |
{ | |
struct timespec temp; | |
if ((end->tv_nsec - start->tv_nsec) < 0) { | |
temp.tv_sec = end->tv_sec - start->tv_sec - 1; | |
temp.tv_nsec = 1000000000 + end->tv_nsec - start->tv_nsec; | |
} else { | |
temp.tv_sec = end->tv_sec - start->tv_sec; | |
temp.tv_nsec = end->tv_nsec - start->tv_nsec; | |
} | |
return (double)(temp.tv_sec * 1000) + (((double)temp.tv_nsec) * 0.000001); | |
} | |
static const char *gen_addr(libusb_device *dev) | |
{ | |
static char buff[4 * 7] = ""; // '255-' x 7 (also gives us nul-terminator for last entry) | |
uint8_t pnums[7]; | |
int pnum_cnt, i; | |
char *p; | |
pnum_cnt = libusb_get_port_numbers(dev, pnums, 7); | |
if (pnum_cnt == LIBUSB_ERROR_OVERFLOW) { | |
// shouldn't happen! | |
strcpy(buff, "<error>"); | |
return buff; | |
} | |
p = buff; | |
for (i = 0; i < pnum_cnt - 1; i++) | |
p += snprintf(p, sizeof(buff) - strlen(buff), "%u.", pnums[i]); | |
snprintf(p, sizeof(buff) - strlen(buff), "%u", pnums[i]); | |
return buff; | |
} | |
// if device is NULL, return device address for device at index idx | |
// if device is not NULL, search by name and return device struct | |
int usb_find_device(unsigned idx, char *addr, unsigned addr_size, void **device, | |
int vid, int pid) | |
{ | |
static libusb_device **devs; | |
libusb_device *dev; | |
struct libusb_device_descriptor desc; | |
int count = 0; | |
size_t i; | |
int res; | |
if (!initialized) { | |
PRINT_INFO(stderr, | |
"Library has not been initialized when loaded\n"); | |
return MVNC_ERROR; | |
} | |
if (!devs || idx == 0) { | |
if (devs) { | |
libusb_free_device_list(devs, 1); | |
devs = 0; | |
} | |
if ((res = libusb_get_device_list(NULL, &devs)) < 0) { | |
PRINT_INFO(stderr, | |
"Unable to get USB device list: %s\n", | |
libusb_strerror(res)); | |
return MVNC_ERROR; | |
} | |
} | |
i = 0; | |
while ((dev = devs[i++]) != NULL) { | |
if ((res = libusb_get_device_descriptor(dev, &desc)) < 0) { | |
PRINT_INFO(stderr, | |
"Unable to get USB device descriptor: %s\n", | |
libusb_strerror(res)); | |
continue; | |
} | |
if ((desc.idVendor == vid && desc.idProduct == pid) || | |
(pid == 0 && vid == 0 && ((desc.idVendor == DEFAULT_VID | |
&& desc.idProduct == DEFAULT_PID) | |
|| (desc.idVendor == | |
DEFAULT_OPEN_VID && | |
desc.idProduct == | |
DEFAULT_OPEN_PID)))) { | |
if (device) { | |
const char *caddr = gen_addr(dev); | |
if (!strcmp(caddr, addr)) { | |
PRINT_DEBUG(stderr, | |
"Found Address: %s - VID/PID %04x:%04x\n", | |
addr, desc.idVendor, desc.idProduct); | |
libusb_ref_device(dev); | |
libusb_free_device_list(devs, 1); | |
*device = dev; | |
devs = 0; | |
return 0; | |
} | |
} else if (idx == count) { | |
const char *caddr = gen_addr(dev); | |
PRINT_DEBUG(stderr, | |
"Device %d Address: %s - VID/PID %04x:%04x\n", | |
idx, caddr, desc.idVendor, desc.idProduct); | |
strncpy(addr, caddr, addr_size); | |
return 0; | |
} | |
count++; | |
} | |
} | |
libusb_free_device_list(devs, 1); | |
devs = 0; | |
return MVNC_DEVICE_NOT_FOUND; | |
} | |
static libusb_device_handle *usb_open_device(libusb_device *dev, uint8_t *endpoint, | |
char *err_string_buff, unsigned buff_size) | |
{ | |
struct libusb_config_descriptor *cdesc; | |
const struct libusb_interface_descriptor *ifdesc; | |
libusb_device_handle *h = NULL; | |
int res, i; | |
if ((res = libusb_open(dev, &h)) < 0) { | |
snprintf(err_string_buff, buff_size, "cannot open device: %s\n", | |
libusb_strerror(res)); | |
return 0; | |
} | |
if ((res = libusb_set_configuration(h, 1)) < 0) { | |
snprintf(err_string_buff, buff_size, | |
"setting config 1 failed: %s\n", libusb_strerror(res)); | |
libusb_close(h); | |
return 0; | |
} | |
if ((res = libusb_claim_interface(h, 0)) < 0) { | |
snprintf(err_string_buff, buff_size, | |
"claiming interface 0 failed: %s\n", | |
libusb_strerror(res)); | |
libusb_close(h); | |
return 0; | |
} | |
if ((res = libusb_get_config_descriptor(dev, 0, &cdesc)) < 0) { | |
snprintf(err_string_buff, buff_size, | |
"Unable to get USB config descriptor: %s\n", | |
libusb_strerror(res)); | |
libusb_close(h); | |
return 0; | |
} | |
ifdesc = cdesc->interface->altsetting; | |
for (i = 0; i < ifdesc->bNumEndpoints; i++) { | |
PRINT_DEBUG(stderr, | |
"Found EP 0x%02x : max packet size is %u bytes\n", | |
ifdesc->endpoint[i].bEndpointAddress, | |
ifdesc->endpoint[i].wMaxPacketSize); | |
if ((ifdesc->endpoint[i].bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) != | |
LIBUSB_TRANSFER_TYPE_BULK) | |
continue; | |
if (! | |
(ifdesc->endpoint[i].bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK)) { | |
*endpoint = ifdesc->endpoint[i].bEndpointAddress; | |
bulk_chunk_len = ifdesc->endpoint[i].wMaxPacketSize; | |
libusb_free_config_descriptor(cdesc); | |
return h; | |
} | |
} | |
libusb_free_config_descriptor(cdesc); | |
strcpy(err_string_buff, "Unable to find BULK OUT endpoint\n"); | |
libusb_close(h); | |
return 0; | |
} | |
// timeout: -1 = no (infinite) timeout, 0 = must happen immediately | |
static int wait_findopen(const char *device_address, int timeout, | |
libusb_device ** dev, libusb_device_handle ** devh, | |
uint8_t * endpoint) | |
{ | |
int i, rc; | |
char last_open_dev_err[128]; | |
usleep(100000); | |
if (mvnc_loglevel > 1) { | |
// I know about switch(), but for some reason -1 is picked up correctly | |
if (timeout == -1) | |
PRINT("Starting wait for connect, no timeout\n"); | |
else if (timeout == 0) | |
PRINT("Trying to connect\n"); | |
else | |
PRINT("Starting wait for connect with %ums timeout\n", timeout * 100); | |
} | |
last_open_dev_err[0] = 0; | |
i = 0; | |
for (;;) { | |
rc = usb_find_device(0, (char *) device_address, 0, | |
(void **) dev, DEFAULT_VID, DEFAULT_PID); | |
if (rc < 0) | |
return MVNC_ERROR; | |
if (!rc) { | |
if ((*devh = usb_open_device(*dev, endpoint, last_open_dev_err, 128))) { | |
PRINT_DEBUG(stderr, "Found and opened device\n"); | |
return 0; | |
} | |
libusb_unref_device(*dev); | |
} | |
if (timeout != -1 && i == timeout) { | |
PRINT_INFO(stderr, "%serror: device not found!\n", | |
last_open_dev_err[0] ? last_open_dev_err : ""); | |
return rc ? MVNC_DEVICE_NOT_FOUND : MVNC_TIMEOUT; | |
} | |
i++; | |
usleep(100000); | |
} | |
} | |
static int send_file(libusb_device_handle * h, uint8_t endpoint, | |
const uint8_t * tx_buf, unsigned file_size) | |
{ | |
const uint8_t *p; | |
int rc; | |
int wb, twb, wbr; | |
double elapsed_time; | |
highres_time_t t1, t2; | |
elapsed_time = 0; | |
twb = 0; | |
p = tx_buf; | |
PRINT_DEBUG(stderr, "Performing bulk write of %u bytes...\n", | |
file_size); | |
while (twb < file_size) { | |
highres_gettime(&t1); | |
wb = file_size - twb; | |
if (wb > bulk_chunk_len) | |
wb = bulk_chunk_len; | |
wbr = 0; | |
rc = libusb_bulk_transfer(h, endpoint, (void *) p, wb, &wbr, | |
write_timeout); | |
if (rc || (wb != wbr)) { | |
if (rc == LIBUSB_ERROR_NO_DEVICE) | |
break; | |
PRINT_INFO(stderr, | |
"bulk write: %s (%d bytes written, %d bytes to write)\n", | |
libusb_strerror(rc), wbr, wb); | |
if (rc == LIBUSB_ERROR_TIMEOUT) | |
return MVNC_TIMEOUT; | |
else | |
return MVNC_ERROR; | |
} | |
highres_gettime(&t2); | |
elapsed_time += highres_elapsed_ms(&t1, &t2); | |
twb += wbr; | |
p += wbr; | |
} | |
PRINT_DEBUG(stderr, | |
"Successfully sent %u bytes of data in %lf ms (%lf MB/s)\n", | |
file_size, elapsed_time, | |
((double) file_size / 1048576.) / (elapsed_time * 0.001)); | |
return 0; | |
} | |
int usb_boot(const char *addr, const void *mvcmd, unsigned size) | |
{ | |
int rc; | |
libusb_device *dev; | |
libusb_device_handle *h; | |
uint8_t endpoint; | |
rc = wait_findopen(addr, connect_timeout, &dev, &h, &endpoint); | |
if (rc) | |
return rc; | |
rc = send_file(h, endpoint, mvcmd, size); | |
libusb_release_interface(h, 0); | |
libusb_close(h); | |
libusb_unref_device(dev); | |
return rc; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment