summaryrefslogtreecommitdiff
path: root/opencl/inc/opencl_device_selection.h
diff options
context:
space:
mode:
authorTor Lillqvist <tml@collabora.com>2014-11-27 15:13:12 +0200
committerTor Lillqvist <tml@collabora.com>2014-11-27 15:32:58 +0200
commita70b717ef872c0ac652883ecd2a82c4cc29763e2 (patch)
tree0975349b1b8798eb9d444d7c91d97d52a2f6fa4e /opencl/inc/opencl_device_selection.h
parentd83b031346799bff0a3298387f76b16baad2e5cf (diff)
Move more Calc-independent OpenCL stuff from the sc to the opencl module
No cleanups yet. Just removed the "sc" namespace parts now when this stuff is no longer Calc-specific. There is still horribly confusing use of the same OpenCLDevice name for both a class and as a namespace, for instance. And the OpenCLDevice class has only public static members even, so effectively it acts as just a namespace anyway... Etc. Change-Id: Idc5f30a721df0101426c676f04a85e02c5dc8443
Diffstat (limited to 'opencl/inc/opencl_device_selection.h')
-rw-r--r--opencl/inc/opencl_device_selection.h641
1 files changed, 641 insertions, 0 deletions
diff --git a/opencl/inc/opencl_device_selection.h b/opencl/inc/opencl_device_selection.h
new file mode 100644
index 000000000000..03373f4a3d0f
--- /dev/null
+++ b/opencl/inc/opencl_device_selection.h
@@ -0,0 +1,641 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_OPENCL_INC_OPENCL_DEVICE_SELECTION_H
+#define INCLUDED_OPENCL_INC_OPENCL_DEVICE_SELECTION_H
+
+#ifdef _MSC_VER
+#define _CRT_SECURE_NO_WARNINGS
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <clew.h>
+
+#define DS_DEVICE_NAME_LENGTH 256
+
+enum ds_status
+{
+ DS_SUCCESS = 0
+ ,DS_INVALID_PROFILE = 1000
+ ,DS_MEMORY_ERROR
+ , DS_INVALID_PERF_EVALUATOR_TYPE
+ , DS_INVALID_PERF_EVALUATOR
+ , DS_PERF_EVALUATOR_ERROR
+ , DS_FILE_ERROR
+ , DS_UNKNOWN_DEVICE_TYPE
+ , DS_PROFILE_FILE_ERROR
+ , DS_SCORE_SERIALIZER_ERROR
+ , DS_SCORE_DESERIALIZER_ERROR
+};
+
+// device type
+enum ds_device_type
+{
+ DS_DEVICE_NATIVE_CPU = 0
+ ,DS_DEVICE_OPENCL_DEVICE
+};
+
+
+struct ds_device
+{
+ ds_device_type type;
+ cl_device_id oclDeviceID;
+ char* oclPlatformVendor;
+ char* oclDeviceName;
+ char* oclDriverVersion;
+ void* score; // a pointer to the score data, the content/format is application defined
+};
+
+struct ds_profile
+{
+ unsigned int numDevices;
+ ds_device* devices;
+ const char* version;
+};
+
+// deallocate memory used by score
+typedef ds_status(* ds_score_release)(void* score);
+inline ds_status releaseDSProfile(ds_profile* profile, ds_score_release sr)
+{
+ ds_status status = DS_SUCCESS;
+ if (profile != NULL)
+ {
+ if (profile->devices != NULL && sr != NULL)
+ {
+ unsigned int i;
+ for (i = 0; i < profile->numDevices; i++)
+ {
+ free(profile->devices[i].oclPlatformVendor);
+ free(profile->devices[i].oclDeviceName);
+ free(profile->devices[i].oclDriverVersion);
+ status = sr(profile->devices[i].score);
+ if (status != DS_SUCCESS) break;
+ }
+ free(profile->devices);
+ }
+ free(profile);
+ }
+ return status;
+}
+
+
+inline ds_status initDSProfile(ds_profile** p, const char* version)
+{
+ int numDevices;
+ cl_uint numPlatforms;
+ cl_platform_id* platforms = NULL;
+ cl_device_id* devices = NULL;
+ ds_status status = DS_SUCCESS;
+ ds_profile* profile = NULL;
+ unsigned int next;
+ unsigned int i;
+
+ if (p == NULL) return DS_INVALID_PROFILE;
+
+ profile = (ds_profile*)malloc(sizeof(ds_profile));
+ if (profile == NULL) return DS_MEMORY_ERROR;
+
+ memset(profile, 0, sizeof(ds_profile));
+
+ clGetPlatformIDs(0, NULL, &numPlatforms);
+ if (numPlatforms != 0)
+ {
+ platforms = (cl_platform_id*)malloc(numPlatforms * sizeof(cl_platform_id));
+ if (platforms == NULL)
+ {
+ status = DS_MEMORY_ERROR;
+ goto cleanup;
+ }
+ clGetPlatformIDs(numPlatforms, platforms, NULL);
+ }
+
+ numDevices = 0;
+ for (i = 0; i < (unsigned int)numPlatforms; i++)
+ {
+ cl_uint num;
+ clGetDeviceIDs(platforms[i], CL_DEVICE_TYPE_ALL, 0, NULL, &num);
+ numDevices += num;
+ }
+ if (numDevices != 0)
+ {
+ devices = (cl_device_id*)malloc(numDevices * sizeof(cl_device_id));
+ if (devices == NULL)
+ {
+ status = DS_MEMORY_ERROR;
+ goto cleanup;
+ }
+ }
+
+ profile->numDevices = numDevices + 1; // +1 to numDevices to include the native CPU
+ profile->devices = (ds_device*)malloc(profile->numDevices * sizeof(ds_device));
+ if (profile->devices == NULL)
+ {
+ profile->numDevices = 0;
+ status = DS_MEMORY_ERROR;
+ goto cleanup;
+ }
+ memset(profile->devices, 0, profile->numDevices * sizeof(ds_device));
+
+ next = 0;
+ for (i = 0; i < (unsigned int)numPlatforms; i++)
+ {
+ cl_uint num;
+ unsigned j;
+ char vendor[256];
+ if (clGetPlatformInfo(platforms[i], CL_PLATFORM_VENDOR, sizeof(vendor), vendor, NULL) != CL_SUCCESS)
+ vendor[0] = '\0';
+ clGetDeviceIDs(platforms[i], CL_DEVICE_TYPE_ALL, numDevices, devices, &num);
+ for (j = 0; j < num; j++, next++)
+ {
+ char buffer[DS_DEVICE_NAME_LENGTH];
+ size_t length;
+
+ profile->devices[next].type = DS_DEVICE_OPENCL_DEVICE;
+ profile->devices[next].oclDeviceID = devices[j];
+
+ profile->devices[next].oclPlatformVendor = strdup(vendor);
+
+ clGetDeviceInfo(profile->devices[next].oclDeviceID, CL_DEVICE_NAME
+ , DS_DEVICE_NAME_LENGTH, &buffer, NULL);
+ length = strlen(buffer);
+ profile->devices[next].oclDeviceName = (char*)malloc(length + 1);
+ memcpy(profile->devices[next].oclDeviceName, buffer, length + 1);
+
+ clGetDeviceInfo(profile->devices[next].oclDeviceID, CL_DRIVER_VERSION
+ , DS_DEVICE_NAME_LENGTH, &buffer, NULL);
+ length = strlen(buffer);
+ profile->devices[next].oclDriverVersion = (char*)malloc(length + 1);
+ memcpy(profile->devices[next].oclDriverVersion, buffer, length + 1);
+ }
+ }
+ profile->devices[next].type = DS_DEVICE_NATIVE_CPU;
+ profile->version = version;
+
+cleanup:
+ if (platforms) free(platforms);
+ if (devices) free(devices);
+ if (status == DS_SUCCESS)
+ {
+ *p = profile;
+ }
+ else
+ {
+ if (profile)
+ {
+ if (profile->devices) free(profile->devices);
+ free(profile);
+ }
+ }
+ return status;
+}
+
+// Pointer to a function that calculates the score of a device (ex: device->score)
+// update the data size of score. The encoding and the format of the score data
+// is implementation defined. The function should return DS_SUCCESS if there's no error to be reported.
+typedef ds_status(* ds_perf_evaluator)(ds_device* device, void* data);
+
+typedef enum {
+ DS_EVALUATE_ALL
+ , DS_EVALUATE_NEW_ONLY
+} ds_evaluation_type;
+
+inline ds_status profileDevices(ds_profile* profile, const ds_evaluation_type type,
+ ds_perf_evaluator evaluator, void* evaluatorData, unsigned int* numUpdates)
+{
+ ds_status status = DS_SUCCESS;
+ unsigned int i;
+ unsigned int updates = 0;
+
+ if (profile == NULL)
+ {
+ return DS_INVALID_PROFILE;
+ }
+ if (evaluator == NULL)
+ {
+ return DS_INVALID_PERF_EVALUATOR;
+ }
+
+ for (i = 0; i < profile->numDevices; i++)
+ {
+ ds_status evaluatorStatus;
+
+ switch (type)
+ {
+ case DS_EVALUATE_NEW_ONLY:
+ if (profile->devices[i].score != NULL) break;
+ // else fall through
+ case DS_EVALUATE_ALL:
+ evaluatorStatus = evaluator(profile->devices + i, evaluatorData);
+ if (evaluatorStatus != DS_SUCCESS)
+ {
+ status = evaluatorStatus;
+ return status;
+ }
+ updates++;
+ break;
+ default:
+ return DS_INVALID_PERF_EVALUATOR_TYPE;
+ break;
+ };
+ }
+ if (numUpdates) *numUpdates = updates;
+ return status;
+}
+
+
+#define DS_TAG_VERSION "<version>"
+#define DS_TAG_VERSION_END "</version>"
+#define DS_TAG_DEVICE "<device>"
+#define DS_TAG_DEVICE_END "</device>"
+#define DS_TAG_SCORE "<score>"
+#define DS_TAG_SCORE_END "</score>"
+#define DS_TAG_DEVICE_TYPE "<type>"
+#define DS_TAG_DEVICE_TYPE_END "</type>"
+#define DS_TAG_DEVICE_NAME "<name>"
+#define DS_TAG_DEVICE_NAME_END "</name>"
+#define DS_TAG_DEVICE_DRIVER_VERSION "<driver>"
+#define DS_TAG_DEVICE_DRIVER_VERSION_END "</driver>"
+
+#define DS_DEVICE_NATIVE_CPU_STRING "native_cpu"
+
+typedef ds_status(* ds_score_serializer)(ds_device* device, void** serializedScore, unsigned int* serializedScoreSize);
+inline ds_status writeProfileToFile(ds_profile* profile, ds_score_serializer serializer, const char* file)
+{
+ ds_status status = DS_SUCCESS;
+ FILE* profileFile = NULL;
+
+
+ if (profile == NULL) return DS_INVALID_PROFILE;
+
+ profileFile = fopen(file, "wb");
+ if (profileFile == NULL)
+ {
+ status = DS_FILE_ERROR;
+ }
+ else
+ {
+ unsigned int i;
+
+ // write version string
+ fwrite(DS_TAG_VERSION, sizeof(char), strlen(DS_TAG_VERSION), profileFile);
+ fwrite(profile->version, sizeof(char), strlen(profile->version), profileFile);
+ fwrite(DS_TAG_VERSION_END, sizeof(char), strlen(DS_TAG_VERSION_END), profileFile);
+ fwrite("\n", sizeof(char), 1, profileFile);
+
+ for (i = 0; i < profile->numDevices && status == DS_SUCCESS; i++)
+ {
+ void* serializedScore;
+ unsigned int serializedScoreSize;
+
+ fwrite(DS_TAG_DEVICE, sizeof(char), strlen(DS_TAG_DEVICE), profileFile);
+
+ fwrite(DS_TAG_DEVICE_TYPE, sizeof(char), strlen(DS_TAG_DEVICE_TYPE), profileFile);
+ fwrite(&profile->devices[i].type, sizeof(ds_device_type), 1, profileFile);
+ fwrite(DS_TAG_DEVICE_TYPE_END, sizeof(char), strlen(DS_TAG_DEVICE_TYPE_END), profileFile);
+
+ switch (profile->devices[i].type)
+ {
+ case DS_DEVICE_NATIVE_CPU:
+ {
+ // There's no need to emit a device name for the native CPU device.
+ /*
+ fwrite(DS_TAG_DEVICE_NAME, sizeof(char), strlen(DS_TAG_DEVICE_NAME), profileFile);
+ fwrite(DS_DEVICE_NATIVE_CPU_STRING,sizeof(char),strlen(DS_DEVICE_NATIVE_CPU_STRING), profileFile);
+ fwrite(DS_TAG_DEVICE_NAME_END, sizeof(char), strlen(DS_TAG_DEVICE_NAME_END), profileFile);
+ */
+ }
+ break;
+ case DS_DEVICE_OPENCL_DEVICE:
+ {
+ fwrite(DS_TAG_DEVICE_NAME, sizeof(char), strlen(DS_TAG_DEVICE_NAME), profileFile);
+ fwrite(profile->devices[i].oclDeviceName, sizeof(char), strlen(profile->devices[i].oclDeviceName), profileFile);
+ fwrite(DS_TAG_DEVICE_NAME_END, sizeof(char), strlen(DS_TAG_DEVICE_NAME_END), profileFile);
+
+ fwrite(DS_TAG_DEVICE_DRIVER_VERSION, sizeof(char), strlen(DS_TAG_DEVICE_DRIVER_VERSION), profileFile);
+ fwrite(profile->devices[i].oclDriverVersion, sizeof(char), strlen(profile->devices[i].oclDriverVersion), profileFile);
+ fwrite(DS_TAG_DEVICE_DRIVER_VERSION_END, sizeof(char), strlen(DS_TAG_DEVICE_DRIVER_VERSION_END), profileFile);
+ }
+ break;
+ default:
+ break;
+ };
+
+ fwrite(DS_TAG_SCORE, sizeof(char), strlen(DS_TAG_SCORE), profileFile);
+ status = serializer(profile->devices + i, &serializedScore, &serializedScoreSize);
+ if (status == DS_SUCCESS && serializedScore != NULL && serializedScoreSize > 0)
+ {
+ fwrite(serializedScore, sizeof(char), serializedScoreSize, profileFile);
+ free(serializedScore);
+ }
+ fwrite(DS_TAG_SCORE_END, sizeof(char), strlen(DS_TAG_SCORE_END), profileFile);
+ fwrite(DS_TAG_DEVICE_END, sizeof(char), strlen(DS_TAG_DEVICE_END), profileFile);
+ fwrite("\n", sizeof(char), 1, profileFile);
+ }
+ fclose(profileFile);
+ }
+ return status;
+}
+
+
+inline ds_status readProFile(const char* fileName, char** content, size_t* contentSize)
+{
+ FILE* input = NULL;
+ size_t size = 0;
+ char* binary = NULL;
+ long pos = -1;
+
+ *contentSize = 0;
+ *content = NULL;
+
+ input = fopen(fileName, "rb");
+ if (input == NULL)
+ {
+ return DS_FILE_ERROR;
+ }
+
+ fseek(input, 0L, SEEK_END);
+ pos = ftell(input);
+ if (pos < 0)
+ {
+ fclose(input);
+ return DS_FILE_ERROR;
+ }
+
+ size = pos;
+ rewind(input);
+ binary = (char*)malloc(size);
+ if (binary == NULL)
+ {
+ fclose(input);
+ return DS_FILE_ERROR;
+ }
+ size_t bytesRead = fread(binary, sizeof(char), size, input);
+ (void) bytesRead; // avoid warning
+ fclose(input);
+
+ *contentSize = size;
+ *content = binary;
+ return DS_SUCCESS;
+}
+
+
+inline const char* findString(const char* contentStart, const char* contentEnd, const char* string)
+{
+ size_t stringLength;
+ const char* currentPosition;
+ const char* found;
+ found = NULL;
+ stringLength = strlen(string);
+ currentPosition = contentStart;
+ for (currentPosition = contentStart; currentPosition < contentEnd; currentPosition++)
+ {
+ if (*currentPosition == string[0])
+ {
+ if (currentPosition + stringLength < contentEnd)
+ {
+ if (strncmp(currentPosition, string, stringLength) == 0)
+ {
+ found = currentPosition;
+ break;
+ }
+ }
+ }
+ }
+ return found;
+}
+
+
+typedef ds_status(* ds_score_deserializer)(ds_device* device, const unsigned char* serializedScore, unsigned int serializedScoreSize);
+inline ds_status readProfileFromFile(ds_profile* profile, ds_score_deserializer deserializer, const char* file)
+{
+
+ ds_status status = DS_SUCCESS;
+ char* contentStart = NULL;
+ const char* contentEnd = NULL;
+ size_t contentSize;
+
+ if (profile == NULL) return DS_INVALID_PROFILE;
+
+ status = readProFile(file, &contentStart, &contentSize);
+ if (status == DS_SUCCESS)
+ {
+ const char* currentPosition;
+ const char* dataStart;
+ const char* dataEnd;
+ size_t versionStringLength;
+
+ contentEnd = contentStart + contentSize;
+ currentPosition = contentStart;
+
+
+ // parse the version string
+ dataStart = findString(currentPosition, contentEnd, DS_TAG_VERSION);
+ if (dataStart == NULL)
+ {
+ status = DS_PROFILE_FILE_ERROR;
+ goto cleanup;
+ }
+ dataStart += strlen(DS_TAG_VERSION);
+
+ dataEnd = findString(dataStart, contentEnd, DS_TAG_VERSION_END);
+ if (dataEnd == NULL)
+ {
+ status = DS_PROFILE_FILE_ERROR;
+ goto cleanup;
+ }
+
+ versionStringLength = strlen(profile->version);
+ if (versionStringLength != static_cast<size_t>(dataEnd - dataStart)
+ || strncmp(profile->version, dataStart, versionStringLength) != 0)
+ {
+ // version mismatch
+ status = DS_PROFILE_FILE_ERROR;
+ goto cleanup;
+ }
+ currentPosition = dataEnd + strlen(DS_TAG_VERSION_END);
+
+ // parse the device information
+ while (true)
+ {
+ unsigned int i;
+
+ const char* deviceTypeStart;
+ const char* deviceTypeEnd;
+ ds_device_type deviceType;
+
+ const char* deviceNameStart;
+ const char* deviceNameEnd;
+
+ const char* deviceScoreStart;
+ const char* deviceScoreEnd;
+
+ const char* deviceDriverStart;
+ const char* deviceDriverEnd;
+
+ dataStart = findString(currentPosition, contentEnd, DS_TAG_DEVICE);
+ if (dataStart == NULL)
+ {
+ // nothing useful remain, quit...
+ break;
+ }
+ dataStart += strlen(DS_TAG_DEVICE);
+ dataEnd = findString(dataStart, contentEnd, DS_TAG_DEVICE_END);
+ if (dataEnd == NULL)
+ {
+ status = DS_PROFILE_FILE_ERROR;
+ goto cleanup;
+ }
+
+ // parse the device type
+ deviceTypeStart = findString(dataStart, contentEnd, DS_TAG_DEVICE_TYPE);
+ if (deviceTypeStart == NULL)
+ {
+ status = DS_PROFILE_FILE_ERROR;
+ goto cleanup;
+ }
+ deviceTypeStart += strlen(DS_TAG_DEVICE_TYPE);
+ deviceTypeEnd = findString(deviceTypeStart, contentEnd, DS_TAG_DEVICE_TYPE_END);
+ if (deviceTypeEnd == NULL)
+ {
+ status = DS_PROFILE_FILE_ERROR;
+ goto cleanup;
+ }
+ memcpy(&deviceType, deviceTypeStart, sizeof(ds_device_type));
+
+
+ // parse the device name
+ if (deviceType == DS_DEVICE_OPENCL_DEVICE)
+ {
+
+ deviceNameStart = findString(dataStart, contentEnd, DS_TAG_DEVICE_NAME);
+ if (deviceNameStart == NULL)
+ {
+ status = DS_PROFILE_FILE_ERROR;
+ goto cleanup;
+ }
+ deviceNameStart += strlen(DS_TAG_DEVICE_NAME);
+ deviceNameEnd = findString(deviceNameStart, contentEnd, DS_TAG_DEVICE_NAME_END);
+ if (deviceNameEnd == NULL)
+ {
+ status = DS_PROFILE_FILE_ERROR;
+ goto cleanup;
+ }
+
+
+ deviceDriverStart = findString(dataStart, contentEnd, DS_TAG_DEVICE_DRIVER_VERSION);
+ if (deviceDriverStart == NULL)
+ {
+ status = DS_PROFILE_FILE_ERROR;
+ goto cleanup;
+ }
+ deviceDriverStart += strlen(DS_TAG_DEVICE_DRIVER_VERSION);
+ deviceDriverEnd = findString(deviceDriverStart, contentEnd, DS_TAG_DEVICE_DRIVER_VERSION_END);
+ if (deviceDriverEnd == NULL)
+ {
+ status = DS_PROFILE_FILE_ERROR;
+ goto cleanup;
+ }
+
+
+ // check if this device is on the system
+ for (i = 0; i < profile->numDevices; i++)
+ {
+ if (profile->devices[i].type == DS_DEVICE_OPENCL_DEVICE)
+ {
+ size_t actualDeviceNameLength;
+ size_t driverVersionLength;
+
+ actualDeviceNameLength = strlen(profile->devices[i].oclDeviceName);
+ driverVersionLength = strlen(profile->devices[i].oclDriverVersion);
+ if (actualDeviceNameLength == static_cast<size_t>(deviceNameEnd - deviceNameStart)
+ && driverVersionLength == static_cast<size_t>(deviceDriverEnd - deviceDriverStart)
+ && strncmp(profile->devices[i].oclDeviceName, deviceNameStart, actualDeviceNameLength) == 0
+ && strncmp(profile->devices[i].oclDriverVersion, deviceDriverStart, driverVersionLength) == 0)
+ {
+
+ deviceScoreStart = findString(dataStart, contentEnd, DS_TAG_SCORE);
+ if (deviceScoreStart == NULL)
+ {
+ status = DS_PROFILE_FILE_ERROR;
+ goto cleanup;
+ }
+ deviceScoreStart += strlen(DS_TAG_SCORE);
+ deviceScoreEnd = findString(deviceScoreStart, contentEnd, DS_TAG_SCORE_END);
+ status = deserializer(profile->devices + i, (const unsigned char*)deviceScoreStart, deviceScoreEnd - deviceScoreStart);
+ if (status != DS_SUCCESS)
+ {
+ goto cleanup;
+ }
+ }
+ }
+ }
+
+ }
+ else if (deviceType == DS_DEVICE_NATIVE_CPU)
+ {
+ for (i = 0; i < profile->numDevices; i++)
+ {
+ if (profile->devices[i].type == DS_DEVICE_NATIVE_CPU)
+ {
+ deviceScoreStart = findString(dataStart, contentEnd, DS_TAG_SCORE);
+ if (deviceScoreStart == NULL)
+ {
+ status = DS_PROFILE_FILE_ERROR;
+ goto cleanup;
+ }
+ deviceScoreStart += strlen(DS_TAG_SCORE);
+ deviceScoreEnd = findString(deviceScoreStart, contentEnd, DS_TAG_SCORE_END);
+ status = deserializer(profile->devices + i, (const unsigned char*)deviceScoreStart, deviceScoreEnd - deviceScoreStart);
+ if (status != DS_SUCCESS)
+ {
+ goto cleanup;
+ }
+ }
+ }
+ }
+
+ // skip over the current one to find the next device
+ currentPosition = dataEnd + strlen(DS_TAG_DEVICE_END);
+ }
+ }
+cleanup:
+ if (contentStart != NULL) free(contentStart);
+ if (status != DS_SUCCESS)
+ return status;
+
+ // Check that all the devices present had valid cached scores. If
+ // not, return DS_INVALID_PROFILE and let the caller re-evaluate
+ // scores for present devices, and write a new profile file.
+ for (unsigned int i = 0; i < profile->numDevices; i++)
+ if (profile->devices[i].score == NULL)
+ return DS_INVALID_PROFILE;
+
+ return DS_SUCCESS;
+}
+
+inline ds_status getNumDeviceWithEmptyScore(ds_profile* profile, unsigned int* num)
+{
+ unsigned int i;
+ if (profile == NULL || num == NULL) return DS_MEMORY_ERROR;
+ *num = 0;
+ for (i = 0; i < profile->numDevices; i++)
+ {
+ if (profile->devices[i].score == NULL)
+ {
+ (*num)++;
+ }
+ }
+ return DS_SUCCESS;
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */