OpenVINO Inference Engine之GetAvailableDevices

在<学习OpenVINO笔记之Inference Engine Device Query API>中的查询设备用例中,有使用到查看当前有哪些设备可以上线接口GetAvailableDevices()函数,通过该接口流程可以查看对当前已上线的设备是怎么管理的,对后面移植openvino非常重要,过下源码对该流程进行分析。

Core::GetAvailableDevices() 

对外提供的接口是在Core类中GetAvailableDevices()API,代码如下:

OpenVINO Inference Engine之GetAvailableDevices_第1张图片

首先是调用Impl类中的GetListOfDevicesInRegistry()接口,获取到已知的注册的设备类型

pluginRegistry类为plugins.xml文件中注册的设备类型,对每个设备类型进行单独轮询调用GetMetric接口。

Core::GetMetric

GetMetric()获取该类型设备实际能力

OpenVINO Inference Engine之GetAvailableDevices_第2张图片

调用的是Engine中的Engine::GetMetric方法

Engine::GetMetric

Engine::GetMetric()函数处理如下

OpenVINO Inference Engine之GetAvailableDevices_第3张图片

 最后调用的是_metrics,而

OpenVINO Inference Engine之GetAvailableDevices_第4张图片

 _metrics指向的是MyriadMetrics类地址:

OpenVINO Inference Engine之GetAvailableDevices_第5张图片

整个调用过程太繁琐,最后使用的Mvnc类中的接口

OpenVINO Inference Engine之GetAvailableDevices_第6张图片

 最后调用ncAvailableDevices接口,获取到当前上线的设备数量,该接口属于第三方库movidius中API.

ncAvailableDevices

接下来就要涉及到movidius sdk host端源码,ncAvailableDevices为获取到当前上线的movidius数量:

OpenVINO Inference Engine之GetAvailableDevices_第7张图片

最大支持的数量为NC_MAX_DEVICES 32个

#define NC_MAX_DEVICES         (32) 

设置当前获取到的协议类型为X_LINK_ANY_PROTOCOL,支持任何类型

OpenVINO Inference Engine之GetAvailableDevices_第8张图片

 开始涉及到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接口:

OpenVINO Inference Engine之GetAvailableDevices_第9张图片

getUSBDeviceName

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

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;
}

 

你可能感兴趣的:(#,Movidius)