Skip to content

Instantly share code, notes, and snippets.

@milosgajdos
Created August 17, 2018 16:56
Show Gist options
  • Save milosgajdos/dbb11a9b2e0cd70805f385234ee645eb to your computer and use it in GitHub Desktop.
Save milosgajdos/dbb11a9b2e0cd70805f385234ee645eb to your computer and use it in GitHub Desktop.
/*
* 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;
}
/*
* 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 <david.steinberg@movidius.com>
#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