在<学习OpenVINO笔记之Inference Engine Device Query API>中的查询设备用例中,有使用到查看当前有哪些设备可以上线接口GetAvailableDevices()函数,通过该接口流程可以查看对当前已上线的设备是怎么管理的,对后面移植openvino非常重要,过下源码对该流程进行分析。
对外提供的接口是在Core类中GetAvailableDevices()API,代码如下:
首先是调用Impl类中的GetListOfDevicesInRegistry()接口,获取到已知的注册的设备类型
pluginRegistry类为plugins.xml文件中注册的设备类型,对每个设备类型进行单独轮询调用GetMetric接口。
GetMetric()获取该类型设备实际能力
调用的是Engine中的Engine::GetMetric方法
Engine::GetMetric()函数处理如下
最后调用的是_metrics,而
_metrics指向的是MyriadMetrics类地址:
整个调用过程太繁琐,最后使用的Mvnc类中的接口
最后调用ncAvailableDevices接口,获取到当前上线的设备数量,该接口属于第三方库movidius中API.
接下来就要涉及到movidius sdk host端源码,ncAvailableDevices为获取到当前上线的movidius数量:
最大支持的数量为NC_MAX_DEVICES 32个
#define NC_MAX_DEVICES (32)
设置当前获取到的协议类型为X_LINK_ANY_PROTOCOL,支持任何类型
开始涉及到X_LINK协议,该协议是将不同的硬件接口统一封装到X_LINK中,支持USB, PCIE,IPC等类型的接口,X_LINK模块负责提供整个的统一访问movidius平台
最后是调用XLinkPlatformFindArrayOfDevicesNames接口,该接口支持不同的协议类型
xLinkPlatformErrorCode_t XLinkPlatformFindArrayOfDevicesNames(
XLinkDeviceState_t state,
const deviceDesc_t in_deviceRequirements,
deviceDesc_t* out_foundDevice,
const unsigned int devicesArraySize,
unsigned int *out_amountOfFoundDevices) {
memset(out_foundDevice, 0, sizeof(deviceDesc_t) * devicesArraySize);
unsigned int usb_index = 0;
unsigned int pcie_index = 0;
unsigned int both_protocol_index = 0;
// TODO Handle possible errors
switch (in_deviceRequirements.protocol){
case X_LINK_USB_CDC:
case X_LINK_USB_VSC:
while(getUSBDeviceName(
usb_index, state, in_deviceRequirements, &out_foundDevice[usb_index]) ==
X_LINK_PLATFORM_SUCCESS) {
++usb_index;
}
*out_amountOfFoundDevices = usb_index;
return X_LINK_PLATFORM_SUCCESS;
case X_LINK_PCIE:
while(getPCIeDeviceName(
pcie_index, state, in_deviceRequirements, &out_foundDevice[pcie_index]) ==
X_LINK_PLATFORM_SUCCESS) {
++pcie_index;
}
*out_amountOfFoundDevices = pcie_index;
return X_LINK_PLATFORM_SUCCESS;
case X_LINK_ANY_PROTOCOL:
while(getUSBDeviceName(
usb_index, state, in_deviceRequirements,
&out_foundDevice[both_protocol_index]) ==
X_LINK_PLATFORM_SUCCESS) {
++usb_index;
++both_protocol_index;
}
while(getPCIeDeviceName(
pcie_index, state, in_deviceRequirements,
&out_foundDevice[both_protocol_index]) ==
X_LINK_PLATFORM_SUCCESS) {
++pcie_index;
++both_protocol_index;
}
*out_amountOfFoundDevices = both_protocol_index;
return X_LINK_PLATFORM_SUCCESS;
default:
mvLog(MVLOG_WARN, "Unknown protocol");
return X_LINK_PLATFORM_DEVICE_NOT_FOUND;
}
}
其中X_LINK_ANY_PROTOCOL为即支持USB,也支持usb接口:
getUSBDeviceName()为查找usb接口下的所有设备,其驱动接口使用的是libusb
static xLinkPlatformErrorCode_t getUSBDeviceName(int index,
XLinkDeviceState_t state,
const deviceDesc_t in_deviceRequirements,
deviceDesc_t* out_foundDevice) {
ASSERT_X_LINK_PLATFORM(index >= 0);
ASSERT_X_LINK_PLATFORM(out_foundDevice);
int vid = AUTO_VID;
int pid = AUTO_PID;
char name[XLINK_MAX_NAME_SIZE] = {};
int searchByName = 0;
if (strlen(in_deviceRequirements.name) > 0) {
searchByName = 1;
mv_strcpy(name, XLINK_MAX_NAME_SIZE, in_deviceRequirements.name);
}
// Set PID
if (state == X_LINK_BOOTED) {
if (in_deviceRequirements.platform != X_LINK_ANY_PLATFORM) {
mvLog(MVLOG_WARN, "Search specific platform for booted device unavailable");
return X_LINK_PLATFORM_ERROR;
}
pid = DEFAULT_OPENPID;
} else {
if (searchByName) {
pid = get_pid_by_name(in_deviceRequirements.name);
} else {
pid = XLinkPlatformToPid(in_deviceRequirements.platform, state);
}
}
#if (!defined(_WIN32) && !defined(_WIN64))
uint16_t bcdusb = -1;
usbBootError_t rc = usb_find_device_with_bcd(
index, name, XLINK_MAX_NAME_SIZE, 0, vid, pid, &bcdusb);
#else
usbBootError_t rc = usb_find_device(
index, name, XLINK_MAX_NAME_SIZE, 0, vid, pid);
#endif
xLinkPlatformErrorCode_t xLinkRc = parseUsbBootError(rc);
if(xLinkRc == X_LINK_PLATFORM_SUCCESS)
{
mv_strcpy(out_foundDevice->name, XLINK_MAX_NAME_SIZE, name);
out_foundDevice->protocol = X_LINK_USB_VSC;
out_foundDevice->platform = XLinkPlatformPidToPlatform(get_pid_by_name(name));
}
return xLinkRc;
}
in_deviceRequirements.name没有指定,为查找到所有的usb下设备,unbutun平台下使用usb_find_device_with_bcd接口
/**
* @brief Find usb device address
* @param input_addr Device name (address) which would be returned. If not empty, we will try to
* find device with this name
*
* @details
* Find any device (device = 0):
*
1) Any myriad device: vid = AUTO_VID & pid = AUTO_PID
*
2) Any not booted myriad device: vid = AUTO_VID & pid = AUTO_UNBOOTED_PID
*
3) Any booted myriad device: vid = AUTO_VID & pid = DEFAULT_OPENPID
*
4) Specific Myriad 2 or Myriad X device: vid = AUTO_VID & pid = DEFAULT_UNBOOTPID_2485 or DEFAULT_UNBOOTPID_2150
*
Find specific device (device != 0):
*
device arg should be not null, search by addr (name) and return device struct
*
* @note
* Index can be used to iterate through all connected myriad devices and save their names.
* It will loop only over suitable devices specified by vid and pid
*/
usbBootError_t usb_find_device_with_bcd(unsigned idx, char *input_addr,
unsigned addrsize, void **device, int vid, int pid, uint16_t* bcdusb) {
if (pthread_mutex_lock(&globalMutex)) {
fprintf(stderr, "Mutex lock failed\n");
return USB_BOOT_ERROR;
}
int searchByName = 0;
static libusb_device **devs = NULL;
libusb_device *dev = NULL;
struct libusb_device_descriptor desc;
int count = 0;
size_t i;
int res;
if (!initialized) {
if (usb_loglevel)
fprintf(stderr, "Library has not been initialized when loaded\n");
if (pthread_mutex_unlock(&globalMutex)) {
fprintf(stderr, "Mutex unlock failed\n");
}
return USB_BOOT_ERROR;
}
if (strlen(input_addr) > 1) {
searchByName = 1;
}
// Update device list if empty or if indx 0
if (!devs || idx == 0) {
if (devs) {
libusb_free_device_list(devs, 1);
devs = 0;
}
if ((res = libusb_get_device_list(NULL, &devs)) < 0) {
if (usb_loglevel)
fprintf(stderr, "Unable to get USB device list: %s\n", libusb_strerror(res));
if (pthread_mutex_unlock(&globalMutex)) {
fprintf(stderr, "Mutex unlock failed\n");
}
return USB_BOOT_ERROR;
}
}
// Loop over all usb devices, increase count only if myriad device
i = 0;
while ((dev = devs[i++]) != NULL) {
if ((res = libusb_get_device_descriptor(dev, &desc)) < 0) {
if (usb_loglevel)
fprintf(stderr, "Unable to get USB device descriptor: %s\n", libusb_strerror(res));
continue;
}
// If found device have the same id and vid as input
if ( (desc.idVendor == vid && desc.idProduct == pid)
// Any myriad device
|| (vid == AUTO_VID && pid == AUTO_PID
&& isMyriadDevice(desc.idVendor, desc.idProduct))
// Any not booted myriad device
|| (vid == AUTO_VID && (pid == AUTO_UNBOOTED_PID)
&& isNotBootedMyriadDevice(desc.idVendor, desc.idProduct))
// Any not booted with specific pid
|| (vid == AUTO_VID && pid == desc.idProduct
&& isNotBootedMyriadDevice(desc.idVendor, desc.idProduct))
// Any booted device
|| (vid == AUTO_VID && pid == DEFAULT_OPENPID
&& isBootedMyriadDevice(desc.idVendor, desc.idProduct)) )
{
if (device) {
const char *dev_addr = gen_addr(dev, get_pid_by_name(input_addr));
if (!strcmp(dev_addr, input_addr)) {
if (usb_loglevel > 1) {
fprintf(stderr, "Found Address: %s - VID/PID %04x:%04x\n",
input_addr, desc.idVendor, desc.idProduct);
}
libusb_ref_device(dev);
libusb_free_device_list(devs, 1);
if (bcdusb)
*bcdusb = desc.bcdUSB;
*device = dev;
devs = 0;
if (pthread_mutex_unlock(&globalMutex)) {
fprintf(stderr, "Mutex unlock failed\n");
}
return USB_BOOT_SUCCESS;
}
} else if (searchByName) {
const char *dev_addr = gen_addr(dev, desc.idProduct);
// If the same add as input
if (!strcmp(dev_addr, input_addr)) {
if (usb_loglevel > 1) {
fprintf(stderr, "Found Address: %s - VID/PID %04x:%04x\n",
input_addr, desc.idVendor, desc.idProduct);
}
if (pthread_mutex_unlock(&globalMutex)) {
fprintf(stderr, "Mutex unlock failed\n");
}
return USB_BOOT_SUCCESS;
}
} else if (idx == count) {
const char *caddr = gen_addr(dev, desc.idProduct);
if (usb_loglevel > 1)
fprintf(stderr, "Device %d Address: %s - VID/PID %04x:%04x\n",
idx, caddr, desc.idVendor, desc.idProduct);
mv_strncpy(input_addr, addrsize, caddr, addrsize - 1);
if (pthread_mutex_unlock(&globalMutex)) {
fprintf(stderr, "Mutex unlock failed\n");
}
return USB_BOOT_SUCCESS;
}
count++;
}
}
libusb_free_device_list(devs, 1);
devs = 0;
if (pthread_mutex_unlock(&globalMutex)) {
fprintf(stderr, "Mutex unlock failed\n");
}
return USB_BOOT_DEVICE_NOT_FOUND;
}
主要使用的是libusb 驱动接口获取设备特性。
getPCIeDeviceName()接口
static xLinkPlatformErrorCode_t getPCIeDeviceName(int index,
XLinkDeviceState_t state,
const deviceDesc_t in_deviceRequirements,
deviceDesc_t* out_foundDevice) {
ASSERT_X_LINK_PLATFORM(index >= 0);
ASSERT_X_LINK_PLATFORM(out_foundDevice);
ASSERT_X_LINK_PLATFORM(in_deviceRequirements.platform != X_LINK_MYRIAD_2);
char name[XLINK_MAX_NAME_SIZE] = {};
if (strlen(in_deviceRequirements.name) > 0) {
mv_strcpy(name, XLINK_MAX_NAME_SIZE, in_deviceRequirements.name);
}
pcieHostError_t rc = pcie_find_device_port(
index, name, XLINK_MAX_NAME_SIZE, XLinkStateToPciePlatformState(state));
xLinkPlatformErrorCode_t xLinkRc = parsePCIeHostError(rc);
if(xLinkRc == X_LINK_PLATFORM_SUCCESS)
{
mv_strcpy(out_foundDevice->name, XLINK_MAX_NAME_SIZE, name);
out_foundDevice->protocol = X_LINK_PCIE;
out_foundDevice->platform = X_LINK_MYRIAD_X;
}
return xLinkRc;
}
最终调用pcie_find_device_port接口
pcieHostError_t pcie_find_device_port(
int index, char* port_name, int name_length, const pciePlatformState_t requiredState) {
ASSERT_X_LINK_PLATFORM(port_name);
ASSERT_X_LINK_PLATFORM(index >= 0);
ASSERT_X_LINK_PLATFORM(name_length > 0);
pcieHostError_t rc = PCIE_HOST_DEVICE_NOT_FOUND;
char found_device[XLINK_MAX_NAME_SIZE] = { 0 };
pciePlatformState_t platformState;
#if (defined(_WIN32) || defined(_WIN64))
int amoutOfMyriadPCIeDevices = pci_count_devices(PCIE_VENDOR_ID, PCIE_DEVICE_ID);
if (amoutOfMyriadPCIeDevices == 0)
return PCIE_HOST_DEVICE_NOT_FOUND;
int amountOfSuitableDevices = 0;
int deviceCount = 0;
while (deviceCount < amoutOfMyriadPCIeDevices) {
snprintf(found_device, XLINK_MAX_NAME_SIZE, "%s%d", "\\\\.\\mxlink", deviceCount);
// Get state of device
if (pcie_get_device_state(found_device, &platformState) != 0) {
return PCIE_HOST_ERROR; // Get device state step failed
}
// Found device suits requested state
if (platformState == requiredState || requiredState == PCIE_PLATFORM_ANY_STATE) {
// If port_name is specified, we search for specific device
if (strnlen(port_name, name_length) > 1 &&
strncmp(port_name, found_device, name_length) == 0) {
rc = PCIE_HOST_SUCCESS;
break;
// Trying to find device which suits requirements and index
}
else if (amountOfSuitableDevices == index) {
mv_strncpy(port_name, name_length,
found_device, XLINK_MAX_NAME_SIZE - 1);
rc = PCIE_HOST_SUCCESS;
break;
}
++amountOfSuitableDevices;
}
++deviceCount;
}
return rc;
#else
struct dirent *entry;
DIR *dp;
dp = opendir("/sys/class/mxlk/");
if (dp == NULL) {
return PCIE_HOST_DRIVER_NOT_LOADED;
}
// All entries in this (virtual) directory are generated when the driver
// is loaded, and correspond 1:1 to entries in /dev/
int device_cnt = 0;
while((entry = readdir(dp))) {
// Compare the beginning of the name to make sure it is a device name
if (strncmp(entry->d_name, "mxlk", 4) == 0)
{
// Save name
snprintf(found_device, name_length, "/dev/%s", entry->d_name);
// Get state of device
if (pcie_get_device_state(found_device, &platformState) != 0) {
closedir(dp);
return PCIE_HOST_ERROR; // Get device state step failed
}
// Found device suits requested state
if (platformState == requiredState || requiredState == PCIE_PLATFORM_ANY_STATE) {
// If port_name is specified, we search for specific device
if (strnlen(port_name, name_length) > 1 &&
strncmp(port_name, found_device, name_length) == 0) {
rc = PCIE_HOST_SUCCESS;
break;
// Trying to find device which suits requirements and index
} else if (device_cnt == index){
mv_strncpy(port_name, name_length,
found_device, XLINK_MAX_NAME_SIZE - 1);
rc = PCIE_HOST_SUCCESS;
break;
}
++device_cnt;
}
}
}
closedir(dp);
return rc;
#endif // (!defined(_WIN32) && !defined(_WIN64))
}
上述是整个GetAvailableDevices工作流程,七拐八绕,比较麻烦
其他特性的获取同样最后都是调用MyriadMetrics, 其实出了获取设备name ,id,其他特性都是写死的
//------------------------------------------------------------------------------
// Implementation of methods of class MyriadMetrics
//------------------------------------------------------------------------------
MyriadMetrics::MyriadMetrics() {
_supportedMetrics = {
METRIC_KEY(AVAILABLE_DEVICES),
METRIC_KEY(FULL_DEVICE_NAME),
METRIC_KEY(SUPPORTED_METRICS),
METRIC_KEY(SUPPORTED_CONFIG_KEYS),
METRIC_KEY(OPTIMIZATION_CAPABILITIES),
METRIC_KEY(RANGE_FOR_ASYNC_INFER_REQUESTS)
};
_supportedConfigKeys = {
KEY_VPU_HW_STAGES_OPTIMIZATION,
KEY_VPU_LOG_LEVEL,
KEY_VPU_PRINT_RECEIVE_TENSOR_TIME,
KEY_VPU_NETWORK_CONFIG,
KEY_VPU_COMPUTE_LAYOUT,
KEY_VPU_CUSTOM_LAYERS,
KEY_VPU_IGNORE_IR_STATISTIC,
KEY_VPU_MYRIAD_FORCE_RESET,
KEY_VPU_MYRIAD_PLATFORM,
KEY_EXCLUSIVE_ASYNC_REQUESTS,
KEY_LOG_LEVEL,
KEY_PERF_COUNT,
KEY_CONFIG_FILE,
KEY_DEVICE_ID
};
_optimizationCapabilities = { METRIC_VALUE(FP16) };
_rangeForAsyncInferRequests = RangeType(3, 6, 1);
_idToDeviceFullNameMap = {
{"5", "Intel Movidius Myriad 2 VPU"},
{"8", "Intel Movidius Myriad X VPU"},
};
}
std::vector MyriadMetrics::AvailableDevicesNames(
const std::shared_ptr &mvnc,
const std::vector &devicePool) const {
std::vector availableDevices;
auto unbootedDevices = mvnc->AvailableDevicesNames();
availableDevices.insert(availableDevices.begin(),
unbootedDevices.begin(), unbootedDevices.end());
for (auto & device : devicePool) {
availableDevices.push_back(device->_name);
}
std::sort(availableDevices.begin(), availableDevices.end());
return availableDevices;
}
std::string MyriadMetrics::FullName(std::string deviceName) const {
std::string nameDelimiter("-ma");
unsigned int indexLenght = 4;
unsigned int placeOfTypeId = 2;
auto indexStr = deviceName;
indexStr.erase(0, indexStr.find(nameDelimiter) + nameDelimiter.length());
if (indexLenght != indexStr.length()) {
return deviceName;
} else {
auto myriadId = std::string(1, indexStr[placeOfTypeId]);
if (_idToDeviceFullNameMap.count(myriadId)) {
return _idToDeviceFullNameMap.at(myriadId);
}
}
return deviceName;
}
const std::vector& MyriadMetrics::SupportedMetrics() const {
return _supportedMetrics;
}
const std::vector& MyriadMetrics::SupportedConfigKeys() const {
return _supportedConfigKeys;
}
const std::vector& MyriadMetrics::OptimizationCapabilities() const {
return _optimizationCapabilities;
}
RangeType MyriadMetrics::RangeForAsyncInferRequests(
const std::map& config) const {
auto throughput_streams_str = config.find(KEY_VPU_MYRIAD_THROUGHPUT_STREAMS);
if (throughput_streams_str != config.end()) {
try {
int throughput_streams = std::stoi(throughput_streams_str->second);
if (throughput_streams > 0) {
return RangeType(throughput_streams+1, throughput_streams*3, 1);
}
}
catch(...) {
THROW_IE_EXCEPTION << "Invalid config value for VPU_MYRIAD_THROUGHPUT_STREAMS, can't cast to int";
}
}
return _rangeForAsyncInferRequests;
}