// Copyright(c) 2018-2020, Intel Corporation
//
// Redistribution  and  use  in source  and  binary  forms,  with  or  without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of  source code  must retain the  above copyright notice,
//   this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
//   this list of conditions and the following disclaimer in the documentation
//   and/or other materials provided with the distribution.
// * Neither the name  of Intel Corporation  nor the names of its contributors
//   may be used to  endorse or promote  products derived  from this  software
//   without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,  BUT NOT LIMITED TO,  THE
// IMPLIED WARRANTIES OF  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED.  IN NO EVENT  SHALL THE COPYRIGHT OWNER  OR CONTRIBUTORS BE
// LIABLE  FOR  ANY  DIRECT,  INDIRECT,  INCIDENTAL,  SPECIAL,  EXEMPLARY,  OR
// CONSEQUENTIAL  DAMAGES  (INCLUDING,  BUT  NOT LIMITED  TO,  PROCUREMENT  OF
// SUBSTITUTE GOODS OR SERVICES;  LOSS OF USE,  DATA, OR PROFITS;  OR BUSINESS
// INTERRUPTION)  HOWEVER CAUSED  AND ON ANY THEORY  OF LIABILITY,  WHETHER IN
// CONTRACT,  STRICT LIABILITY,  OR TORT  (INCLUDING NEGLIGENCE  OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,  EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /* HAVE_CONFIG_H */

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif // _GNU_SOURCE

#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include <opae/enum.h>

#include "props.h"

struct _fpga_properties *opae_properties_create(void)
{
	struct _fpga_properties *props;
	pthread_mutexattr_t mattr;
	int err;

	props = (struct _fpga_properties *)calloc(
		1, sizeof(struct _fpga_properties));

	if (!props)
		return NULL;

	props->magic = FPGA_PROPERTY_MAGIC;

	if (pthread_mutexattr_init(&mattr)) {
		OPAE_ERR("pthread_mutexattr_init() failed");
		goto out_free;
	}

	if (pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_RECURSIVE)) {
		OPAE_ERR("pthread_mutexattr_settype() failed");
		goto out_destroy_attr;
	}

	if (pthread_mutex_init(&props->lock, &mattr)) {
		OPAE_ERR("pthread_mutex_init() failed");
		goto out_destroy_attr;
	}

	pthread_mutexattr_destroy(&mattr);

	return props;

out_destroy_attr:
	err = pthread_mutexattr_destroy(&mattr);
	if (err)
		OPAE_ERR("pthread_mutexattr_destroy() failed: %s",
			 strerror(err));
out_free:
	free(props);
	return NULL;
}

fpga_result __OPAE_API__ fpgaDestroyProperties(fpga_properties *prop)
{
	struct _fpga_properties *p;
	int err;

	ASSERT_NOT_NULL(prop);

	p = opae_validate_and_lock_properties(*prop);

	ASSERT_NOT_NULL(p);

	if (FIELD_VALID(p, FPGA_PROPERTY_PARENT) &&
	    (p->flags & OPAE_PROPERTIES_FLAG_PARENT_ALLOC)) {
		// The parent token has a wrapper that we allocated.
		// Free it.
		opae_wrapped_token *wrapped_token =
			opae_validate_wrapped_token(p->parent);
		if (wrapped_token)
			opae_destroy_wrapped_token(wrapped_token);
	}

	p->magic = 0;

	opae_mutex_unlock(err, &p->lock);

	err = pthread_mutex_destroy(&p->lock);
	if (err)
		OPAE_ERR("pthread_mutex_destroy() failed: %s", strerror(err));

	free(p);
	*prop = NULL;

	return FPGA_OK;
}

fpga_result __OPAE_API__ fpgaCloneProperties(fpga_properties src,
					     fpga_properties *dst)
{
	int err;
	struct _fpga_properties *clone;
	pthread_mutex_t save_lock;
	struct _fpga_properties *p;

	ASSERT_NOT_NULL(dst);

	p = opae_validate_and_lock_properties(src);

	ASSERT_NOT_NULL(p);

	clone = opae_properties_create();
	if (!clone) {
		opae_mutex_unlock(err, &p->lock);
		return FPGA_EXCEPTION;
	}

	save_lock = clone->lock;

	*clone = *p;
	clone->lock = save_lock;

	*dst = clone;

	opae_mutex_unlock(err, &p->lock);

	return FPGA_OK;
}

fpga_result __OPAE_API__ fpgaClearProperties(fpga_properties props)
{
	int err;
	struct _fpga_properties *p = opae_validate_and_lock_properties(props);

	ASSERT_NOT_NULL(p);

	p->valid_fields = 0;

	opae_mutex_unlock(err, &p->lock);

	return FPGA_OK;
}

fpga_result __OPAE_API__ fpgaPropertiesGetParent(const fpga_properties prop,
						 fpga_token *parent)
{
	fpga_result res = FPGA_OK;
	int err;
	struct _fpga_properties *p;

	ASSERT_NOT_NULL(parent);

	p = opae_validate_and_lock_properties(prop);

	ASSERT_NOT_NULL(p);

	if (FIELD_VALID(p, FPGA_PROPERTY_PARENT)) {
		res = fpgaCloneToken(p->parent, parent);
		if (res != FPGA_OK)
			OPAE_ERR("cloning token from property");
	} else {
		OPAE_MSG("No parent");
		res = FPGA_NOT_FOUND;
	}

	opae_mutex_unlock(err, &p->lock);

	return res;
}

fpga_result __OPAE_API__ fpgaPropertiesSetParent(fpga_properties prop,
						 fpga_token parent)
{
	int err;
	struct _fpga_properties *p;

	ASSERT_NOT_NULL(parent);

	p = opae_validate_and_lock_properties(prop);

	ASSERT_NOT_NULL(p);

	if (FIELD_VALID(p, FPGA_PROPERTY_PARENT) &&
	    (p->flags & OPAE_PROPERTIES_FLAG_PARENT_ALLOC)) {
		// We have a wrapped parent token that we allocated.
		// Free it.
		opae_wrapped_token *wrapped_token =
			opae_validate_wrapped_token(p->parent);
		if (wrapped_token)
			opae_destroy_wrapped_token(wrapped_token);
	}

	// When explicitly setting a parent token,
	// the caller assumes responsibility for freeing
	// that token.
	p->flags &= ~OPAE_PROPERTIES_FLAG_PARENT_ALLOC;

	p->parent = parent;
	SET_FIELD_VALID(p, FPGA_PROPERTY_PARENT);

	opae_mutex_unlock(err, &p->lock);

	return FPGA_OK;
}

fpga_result __OPAE_API__ fpgaPropertiesGetObjectType(
	const fpga_properties prop, fpga_objtype *objtype)
{
	fpga_result res = FPGA_OK;
	int err;
	struct _fpga_properties *p;

	ASSERT_NOT_NULL(objtype);

	p = opae_validate_and_lock_properties(prop);

	ASSERT_NOT_NULL(p);

	if (FIELD_VALID(p, FPGA_PROPERTY_OBJTYPE)) {
		*objtype = p->objtype;
	} else {
		OPAE_MSG("No object type");
		res = FPGA_NOT_FOUND;
	}

	opae_mutex_unlock(err, &p->lock);

	return res;
}

fpga_result __OPAE_API__ fpgaPropertiesSetObjectType(fpga_properties prop,
						     fpga_objtype objtype)
{
	int err;
	struct _fpga_properties *p = opae_validate_and_lock_properties(prop);

	ASSERT_NOT_NULL(p);

	p->objtype = objtype;
	SET_FIELD_VALID(p, FPGA_PROPERTY_OBJTYPE);

	opae_mutex_unlock(err, &p->lock);

	return FPGA_OK;
}

fpga_result __OPAE_API__ fpgaPropertiesGetSegment(const fpga_properties prop,
						  uint16_t *segment)
{
	fpga_result res = FPGA_OK;
	int err;
	struct _fpga_properties *p;

	ASSERT_NOT_NULL(segment);

	p = opae_validate_and_lock_properties(prop);

	ASSERT_NOT_NULL(p);

	if (FIELD_VALID(p, FPGA_PROPERTY_SEGMENT)) {
		*segment = p->segment;
	} else {
		OPAE_MSG("No segment");
		res = FPGA_NOT_FOUND;
	}

	opae_mutex_unlock(err, &p->lock);

	return res;
}

fpga_result __OPAE_API__ fpgaPropertiesSetSegment(fpga_properties prop,
						  uint16_t segment)
{
	int err;
	struct _fpga_properties *p = opae_validate_and_lock_properties(prop);

	ASSERT_NOT_NULL(p);

	SET_FIELD_VALID(p, FPGA_PROPERTY_SEGMENT);
	p->segment = segment;

	opae_mutex_unlock(err, &p->lock);

	return FPGA_OK;
}

fpga_result __OPAE_API__ fpgaPropertiesGetBus(const fpga_properties prop,
					      uint8_t *bus)
{
	fpga_result res = FPGA_OK;
	int err;
	struct _fpga_properties *p;

	ASSERT_NOT_NULL(bus);

	p = opae_validate_and_lock_properties(prop);

	ASSERT_NOT_NULL(p);

	if (FIELD_VALID(p, FPGA_PROPERTY_BUS)) {
		*bus = p->bus;
	} else {
		OPAE_MSG("No bus");
		res = FPGA_NOT_FOUND;
	}

	opae_mutex_unlock(err, &p->lock);

	return res;
}

fpga_result __OPAE_API__ fpgaPropertiesSetBus(fpga_properties prop,
					      uint8_t bus)
{
	fpga_result res = FPGA_OK;
	int err;
	struct _fpga_properties *p = opae_validate_and_lock_properties(prop);

	ASSERT_NOT_NULL(p);

	SET_FIELD_VALID(p, FPGA_PROPERTY_BUS);
	p->bus = bus;

	opae_mutex_unlock(err, &p->lock);

	return res;
}

fpga_result __OPAE_API__ fpgaPropertiesGetDevice(const fpga_properties prop,
						 uint8_t *device)
{
	fpga_result res = FPGA_OK;
	int err;
	struct _fpga_properties *p;

	ASSERT_NOT_NULL(device);

	p = opae_validate_and_lock_properties(prop);

	ASSERT_NOT_NULL(p);

	if (FIELD_VALID(p, FPGA_PROPERTY_DEVICE)) {
		*device = p->device;
	} else {
		OPAE_MSG("No device");
		res = FPGA_NOT_FOUND;
	}

	opae_mutex_unlock(err, &p->lock);

	return res;
}

fpga_result __OPAE_API__ fpgaPropertiesSetDevice(fpga_properties prop,
						 uint8_t device)
{
	fpga_result res = FPGA_OK;
	int err;
	struct _fpga_properties *p = opae_validate_and_lock_properties(prop);

	ASSERT_NOT_NULL(p);

	SET_FIELD_VALID(p, FPGA_PROPERTY_DEVICE);
	p->device = device;

	opae_mutex_unlock(err, &p->lock);

	return res;
}

fpga_result __OPAE_API__ fpgaPropertiesGetFunction(const fpga_properties prop,
						   uint8_t *function)
{
	fpga_result res = FPGA_OK;
	int err;
	struct _fpga_properties *p;

	ASSERT_NOT_NULL(function);

	p = opae_validate_and_lock_properties(prop);

	ASSERT_NOT_NULL(p);

	if (FIELD_VALID(p, FPGA_PROPERTY_FUNCTION)) {
		*function = p->function;
	} else {
		OPAE_MSG("No function");
		res = FPGA_NOT_FOUND;
	}

	opae_mutex_unlock(err, &p->lock);

	return res;
}

fpga_result __OPAE_API__ fpgaPropertiesSetFunction(fpga_properties prop,
						   uint8_t function)
{
	fpga_result res = FPGA_OK;
	int err;
	struct _fpga_properties *p;

	// PCIe supports 8 functions per device.
	if (function > 7) {
		OPAE_MSG("Invalid function number");
		return FPGA_INVALID_PARAM;
	}

	p = opae_validate_and_lock_properties(prop);
	ASSERT_NOT_NULL(p);

	SET_FIELD_VALID(p, FPGA_PROPERTY_FUNCTION);
	p->function = function;

	opae_mutex_unlock(err, &p->lock);

	return res;
}

fpga_result __OPAE_API__ fpgaPropertiesGetSocketID(const fpga_properties prop,
						   uint8_t *socket_id)
{
	fpga_result res = FPGA_OK;
	int err;
	struct _fpga_properties *p;

	ASSERT_NOT_NULL(socket_id);

	p = opae_validate_and_lock_properties(prop);

	ASSERT_NOT_NULL(p);

	if (FIELD_VALID(p, FPGA_PROPERTY_SOCKETID)) {
		*socket_id = p->socket_id;
	} else {
		OPAE_MSG("No socket ID");
		res = FPGA_NOT_FOUND;
	}

	opae_mutex_unlock(err, &p->lock);

	return res;
}

fpga_result __OPAE_API__ fpgaPropertiesSetSocketID(fpga_properties prop,
						   uint8_t socket_id)
{
	fpga_result res = FPGA_OK;
	int err;
	struct _fpga_properties *p = opae_validate_and_lock_properties(prop);

	ASSERT_NOT_NULL(p);

	SET_FIELD_VALID(p, FPGA_PROPERTY_SOCKETID);
	p->socket_id = socket_id;

	opae_mutex_unlock(err, &p->lock);

	return res;
}

fpga_result __OPAE_API__ fpgaPropertiesGetDeviceID(const fpga_properties prop,
						   uint16_t *device_id)
{
	fpga_result res = FPGA_OK;
	int err;
	struct _fpga_properties *p;

	ASSERT_NOT_NULL(device_id);

	p = opae_validate_and_lock_properties(prop);

	ASSERT_NOT_NULL(p);

	if (FIELD_VALID(p, FPGA_PROPERTY_DEVICEID)) {
		*device_id = p->device_id;
	} else {
		OPAE_MSG("No device ID");
		res = FPGA_NOT_FOUND;
	}

	opae_mutex_unlock(err, &p->lock);

	return res;
}

fpga_result __OPAE_API__ fpgaPropertiesSetDeviceID(fpga_properties prop,
						   uint16_t device_id)
{
	fpga_result res = FPGA_OK;
	int err;
	struct _fpga_properties *p = opae_validate_and_lock_properties(prop);

	ASSERT_NOT_NULL(p);

	SET_FIELD_VALID(p, FPGA_PROPERTY_DEVICEID);
	p->device_id = device_id;

	opae_mutex_unlock(err, &p->lock);

	return res;
}

fpga_result __OPAE_API__ fpgaPropertiesGetNumSlots(const fpga_properties prop,
						   uint32_t *num_slots)
{
	fpga_result res = FPGA_OK;
	int err;
	struct _fpga_properties *p;

	ASSERT_NOT_NULL(num_slots);

	p = opae_validate_and_lock_properties(prop);

	ASSERT_NOT_NULL(p);

	if (FIELD_VALID(p, FPGA_PROPERTY_OBJTYPE)
	    && FPGA_DEVICE == p->objtype) {
		if (FIELD_VALID(p, FPGA_PROPERTY_NUM_SLOTS)) {
			*num_slots = p->u.fpga.num_slots;
		} else {
			OPAE_MSG("No number of slots");
			res = FPGA_NOT_FOUND;
		}
	} else {
		OPAE_ERR(
			"Attempting to get num_slots from invalid object type: %d",
			p->objtype);
		res = FPGA_INVALID_PARAM;
	}

	opae_mutex_unlock(err, &p->lock);

	return res;
}

fpga_result __OPAE_API__ fpgaPropertiesSetNumSlots(fpga_properties prop,
						   uint32_t num_slots)
{
	fpga_result res = FPGA_OK;
	int err;
	struct _fpga_properties *p = opae_validate_and_lock_properties(prop);

	ASSERT_NOT_NULL(p);

	if (FIELD_VALID(p, FPGA_PROPERTY_OBJTYPE)
	    && FPGA_DEVICE == p->objtype) {
		SET_FIELD_VALID(p, FPGA_PROPERTY_NUM_SLOTS);
		p->u.fpga.num_slots = num_slots;
	} else {
		OPAE_ERR(
			"Attempting to set num slots on invalid object type: %d",
			p->objtype);
		res = FPGA_INVALID_PARAM;
	}

	opae_mutex_unlock(err, &p->lock);

	return res;
}

fpga_result __OPAE_API__ fpgaPropertiesGetBBSID(const fpga_properties prop,
						uint64_t *bbs_id)
{
	fpga_result res = FPGA_OK;
	int err;
	struct _fpga_properties *p;

	ASSERT_NOT_NULL(bbs_id);

	p = opae_validate_and_lock_properties(prop);

	ASSERT_NOT_NULL(p);

	if (FIELD_VALID(p, FPGA_PROPERTY_OBJTYPE)
	    && FPGA_DEVICE == p->objtype) {
		if (FIELD_VALID(p, FPGA_PROPERTY_BBSID)) {
			*bbs_id = p->u.fpga.bbs_id;
		} else {
			OPAE_MSG("No BBS ID");
			res = FPGA_NOT_FOUND;
		}
	} else {
		OPAE_ERR(
			"Attempting to get BBS ID from invalid object type: %d",
			p->objtype);
		res = FPGA_INVALID_PARAM;
	}

	opae_mutex_unlock(err, &p->lock);

	return res;
}

fpga_result __OPAE_API__ fpgaPropertiesSetBBSID(fpga_properties prop,
						uint64_t bbs_id)
{
	fpga_result res = FPGA_OK;
	int err;
	struct _fpga_properties *p = opae_validate_and_lock_properties(prop);

	ASSERT_NOT_NULL(p);

	if (FIELD_VALID(p, FPGA_PROPERTY_OBJTYPE)
	    && FPGA_DEVICE == p->objtype) {
		SET_FIELD_VALID(p, FPGA_PROPERTY_BBSID);
		p->u.fpga.bbs_id = bbs_id;
	} else {
		OPAE_ERR("Attempting to set BBS ID on invalid object type: %d",
			 p->objtype);
		res = FPGA_INVALID_PARAM;
	}

	opae_mutex_unlock(err, &p->lock);

	return res;
}

fpga_result __OPAE_API__ fpgaPropertiesGetBBSVersion(const fpga_properties prop,
						     fpga_version *bbs_version)
{
	fpga_result res = FPGA_OK;
	int err;
	struct _fpga_properties *p;

	ASSERT_NOT_NULL(bbs_version);

	p = opae_validate_and_lock_properties(prop);

	ASSERT_NOT_NULL(p);

	if (FIELD_VALID(p, FPGA_PROPERTY_OBJTYPE)
	    && FPGA_DEVICE == p->objtype) {
		if (FIELD_VALID(p, FPGA_PROPERTY_BBSVERSION)) {
			*bbs_version = p->u.fpga.bbs_version;
		} else {
			OPAE_MSG("No BBS version");
			res = FPGA_NOT_FOUND;
		}
	} else {
		OPAE_ERR(
			"Attempting to get BBS version from invalid object type: %d",
			p->objtype);
		res = FPGA_INVALID_PARAM;
	}

	opae_mutex_unlock(err, &p->lock);

	return res;
}

fpga_result __OPAE_API__ fpgaPropertiesSetBBSVersion(fpga_properties prop,
						     fpga_version bbs_version)
{
	fpga_result res = FPGA_OK;
	int err;
	struct _fpga_properties *p = opae_validate_and_lock_properties(prop);

	ASSERT_NOT_NULL(p);

	if (FIELD_VALID(p, FPGA_PROPERTY_OBJTYPE)
	    && FPGA_DEVICE == p->objtype) {
		SET_FIELD_VALID(p, FPGA_PROPERTY_BBSVERSION);
		p->u.fpga.bbs_version = bbs_version;
	} else {
		OPAE_ERR(
			"Attempting to set BBS version on invalid object type: %d",
			p->objtype);
		res = FPGA_INVALID_PARAM;
	}

	opae_mutex_unlock(err, &p->lock);

	return res;
}

fpga_result __OPAE_API__ fpgaPropertiesGetVendorID(const fpga_properties prop,
						   uint16_t *vendor_id)
{
	fpga_result res = FPGA_OK;
	int err;
	struct _fpga_properties *p;

	ASSERT_NOT_NULL(vendor_id);

	p = opae_validate_and_lock_properties(prop);

	ASSERT_NOT_NULL(p);

	if (FIELD_VALID(p, FPGA_PROPERTY_VENDORID)) {
		*vendor_id = p->vendor_id;
	} else {
		OPAE_MSG("No vendor ID");
		res = FPGA_NOT_FOUND;
	}

	opae_mutex_unlock(err, &p->lock);

	return res;
}

fpga_result __OPAE_API__ fpgaPropertiesSetVendorID(fpga_properties prop,
						   uint16_t vendor_id)
{
	fpga_result res = FPGA_OK;
	int err;
	struct _fpga_properties *p = opae_validate_and_lock_properties(prop);

	ASSERT_NOT_NULL(p);

	SET_FIELD_VALID(p, FPGA_PROPERTY_VENDORID);
	p->vendor_id = vendor_id;

	opae_mutex_unlock(err, &p->lock);

	return res;
}

fpga_result __OPAE_API__ fpgaPropertiesGetModel(const fpga_properties prop,
						char *model)
{
	UNUSED_PARAM(prop);
	UNUSED_PARAM(model);
	OPAE_MSG("Model not supported");
	return FPGA_NOT_SUPPORTED;
}

fpga_result __OPAE_API__ fpgaPropertiesSetModel(fpga_properties prop,
						char *model)
{
	UNUSED_PARAM(prop);
	UNUSED_PARAM(model);
	OPAE_MSG("Model not supported");
	return FPGA_NOT_SUPPORTED;
}

fpga_result __OPAE_API__ fpgaPropertiesGetLocalMemorySize(
	const fpga_properties prop, uint64_t *local_memory_size)
{
	UNUSED_PARAM(prop);
	UNUSED_PARAM(local_memory_size);
	OPAE_MSG("Local memory not supported");
	return FPGA_NOT_SUPPORTED;
}

fpga_result __OPAE_API__ fpgaPropertiesSetLocalMemorySize(fpga_properties prop,
						uint64_t local_memory_size)
{
	UNUSED_PARAM(prop);
	UNUSED_PARAM(local_memory_size);
	OPAE_MSG("Local memory not supported");
	return FPGA_NOT_SUPPORTED;
}

fpga_result __OPAE_API__ fpgaPropertiesGetCapabilities(
	const fpga_properties prop, uint64_t *capabilities)
{
	UNUSED_PARAM(prop);
	UNUSED_PARAM(capabilities);
	OPAE_MSG("Capabilities not supported");
	return FPGA_NOT_SUPPORTED;
}

fpga_result __OPAE_API__ fpgaPropertiesSetCapabilities(fpga_properties prop,
						       uint64_t capabilities)
{
	UNUSED_PARAM(prop);
	UNUSED_PARAM(capabilities);
	OPAE_MSG("Capabilities not supported");
	return FPGA_NOT_SUPPORTED;
}

fpga_result __OPAE_API__ fpgaPropertiesGetGUID(const fpga_properties prop,
					       fpga_guid *guid)
{
	fpga_result res = FPGA_OK;
	int err;
	struct _fpga_properties *p;

	ASSERT_NOT_NULL(guid);

	p = opae_validate_and_lock_properties(prop);

	ASSERT_NOT_NULL(p);

	if (FIELD_VALID(p, FPGA_PROPERTY_GUID)) {
		memcpy(*guid, p->guid, sizeof(fpga_guid));
	} else {
		OPAE_MSG("No GUID");
		res = FPGA_NOT_FOUND;
	}

	opae_mutex_unlock(err, &p->lock);

	return res;
}

fpga_result __OPAE_API__ fpgaPropertiesSetGUID(fpga_properties prop,
					       fpga_guid guid)
{
	fpga_result res = FPGA_OK;
	int err;
	struct _fpga_properties *p = opae_validate_and_lock_properties(prop);

	ASSERT_NOT_NULL(p);

	SET_FIELD_VALID(p, FPGA_PROPERTY_GUID);

	memcpy(p->guid, guid, sizeof(fpga_guid));

	opae_mutex_unlock(err, &p->lock);

	return res;
}

fpga_result __OPAE_API__ fpgaPropertiesGetNumMMIO(const fpga_properties prop,
						  uint32_t *mmio_spaces)
{
	fpga_result res = FPGA_OK;
	int err;
	struct _fpga_properties *p;

	ASSERT_NOT_NULL(mmio_spaces);

	p = opae_validate_and_lock_properties(prop);

	ASSERT_NOT_NULL(p);

	if (FIELD_VALID(p, FPGA_PROPERTY_OBJTYPE)
	    && FPGA_ACCELERATOR == p->objtype) {
		if (FIELD_VALID(p, FPGA_PROPERTY_NUM_MMIO)) {
			*mmio_spaces = p->u.accelerator.num_mmio;
		} else {
			OPAE_MSG("No MMIO spaces");
			res = FPGA_NOT_FOUND;
		}
	} else {
		OPAE_ERR(
			"Attempting to get number of MMIO spaces from invalid object type: %d",
			p->objtype);
		res = FPGA_INVALID_PARAM;
	}

	opae_mutex_unlock(err, &p->lock);

	return res;
}

fpga_result __OPAE_API__ fpgaPropertiesSetNumMMIO(fpga_properties prop,
						  uint32_t mmio_spaces)
{
	fpga_result res = FPGA_OK;
	int err;
	struct _fpga_properties *p = opae_validate_and_lock_properties(prop);

	ASSERT_NOT_NULL(p);

	if (FIELD_VALID(p, FPGA_PROPERTY_OBJTYPE)
	    && FPGA_ACCELERATOR == p->objtype) {
		SET_FIELD_VALID(p, FPGA_PROPERTY_NUM_MMIO);
		p->u.accelerator.num_mmio = mmio_spaces;
	} else {
		OPAE_ERR(
			"Attempting to set number of MMIO spaces on invalid object type: %d",
			p->objtype);
		res = FPGA_INVALID_PARAM;
	}

	opae_mutex_unlock(err, &p->lock);

	return res;
}

fpga_result __OPAE_API__ fpgaPropertiesGetNumInterrupts(
	const fpga_properties prop, uint32_t *num_interrupts)
{
	fpga_result res = FPGA_OK;
	int err;
	struct _fpga_properties *p;

	ASSERT_NOT_NULL(num_interrupts);

	p = opae_validate_and_lock_properties(prop);

	ASSERT_NOT_NULL(p);

	if (FIELD_VALID(p, FPGA_PROPERTY_OBJTYPE)
	    && FPGA_ACCELERATOR == p->objtype) {
		if (FIELD_VALID(p, FPGA_PROPERTY_NUM_INTERRUPTS)) {
			*num_interrupts = p->u.accelerator.num_interrupts;
		} else {
			OPAE_MSG("No interrupts");
			res = FPGA_NOT_FOUND;
		}
	} else {
		OPAE_ERR(
			"Attempting to get number of interrupts from invalid object type: %d",
			p->objtype);
		res = FPGA_INVALID_PARAM;
	}

	opae_mutex_unlock(err, &p->lock);

	return res;
}

fpga_result __OPAE_API__ fpgaPropertiesSetNumInterrupts(fpga_properties prop,
							uint32_t num_interrupts)
{
	fpga_result res = FPGA_OK;
	int err;
	struct _fpga_properties *p = opae_validate_and_lock_properties(prop);

	ASSERT_NOT_NULL(p);

	if (FIELD_VALID(p, FPGA_PROPERTY_OBJTYPE)
	    && FPGA_ACCELERATOR == p->objtype) {
		SET_FIELD_VALID(p, FPGA_PROPERTY_NUM_INTERRUPTS);
		p->u.accelerator.num_interrupts = num_interrupts;
	} else {
		OPAE_ERR(
			"Attempting to set number of interrupts on invalid object type: %d",
			p->objtype);
		res = FPGA_INVALID_PARAM;
	}

	opae_mutex_unlock(err, &p->lock);

	return res;
}

fpga_result __OPAE_API__ fpgaPropertiesGetAcceleratorState(
	const fpga_properties prop, fpga_accelerator_state *state)
{
	fpga_result res = FPGA_OK;
	int err;
	struct _fpga_properties *p;

	ASSERT_NOT_NULL(state);

	p = opae_validate_and_lock_properties(prop);

	ASSERT_NOT_NULL(p);

	if (FIELD_VALID(p, FPGA_PROPERTY_OBJTYPE)
	    && FPGA_ACCELERATOR == p->objtype) {
		if (FIELD_VALID(p, FPGA_PROPERTY_ACCELERATOR_STATE)) {
			*state = p->u.accelerator.state;
		} else {
			OPAE_MSG("No accelerator state");
			res = FPGA_NOT_FOUND;
		}
	} else {
		OPAE_ERR("Attempting to get state from invalid object type: %d",
			 p->objtype);
		res = FPGA_INVALID_PARAM;
	}

	opae_mutex_unlock(err, &p->lock);

	return res;
}

fpga_result __OPAE_API__ fpgaPropertiesSetAcceleratorState(
	fpga_properties prop, fpga_accelerator_state state)
{
	fpga_result res = FPGA_OK;
	int err;
	struct _fpga_properties *p = opae_validate_and_lock_properties(prop);

	ASSERT_NOT_NULL(p);

	if (FIELD_VALID(p, FPGA_PROPERTY_OBJTYPE)
	    && FPGA_ACCELERATOR == p->objtype) {
		SET_FIELD_VALID(p, FPGA_PROPERTY_ACCELERATOR_STATE);
		p->u.accelerator.state = state;
	} else {
		OPAE_ERR("Attempting to set state from invalid object type: %d",
			 p->objtype);
		res = FPGA_INVALID_PARAM;
	}

	opae_mutex_unlock(err, &p->lock);

	return res;
}

fpga_result __OPAE_API__ fpgaPropertiesGetObjectID(const fpga_properties prop,
						   uint64_t *object_id)
{
	fpga_result res = FPGA_OK;
	int err;
	struct _fpga_properties *p;

	ASSERT_NOT_NULL(object_id);

	p = opae_validate_and_lock_properties(prop);

	ASSERT_NOT_NULL(p);

	if (FIELD_VALID(p, FPGA_PROPERTY_OBJECTID)) {
		*object_id = p->object_id;
	} else {
		OPAE_MSG("No object ID");
		res = FPGA_NOT_FOUND;
	}

	opae_mutex_unlock(err, &p->lock);

	return res;
}

fpga_result __OPAE_API__ fpgaPropertiesSetObjectID(fpga_properties prop,
						   uint64_t object_id)
{
	fpga_result res = FPGA_OK;
	int err;
	struct _fpga_properties *p = opae_validate_and_lock_properties(prop);

	ASSERT_NOT_NULL(p);

	SET_FIELD_VALID(p, FPGA_PROPERTY_OBJECTID);
	p->object_id = object_id;

	opae_mutex_unlock(err, &p->lock);

	return res;
}

fpga_result __OPAE_API__ fpgaPropertiesGetNumErrors(const fpga_properties prop,
						    uint32_t *num_errors)
{
	fpga_result res = FPGA_OK;
	int err;
	struct _fpga_properties *p;

	ASSERT_NOT_NULL(num_errors);

	p = opae_validate_and_lock_properties(prop);

	ASSERT_NOT_NULL(p);

	if (FIELD_VALID(p, FPGA_PROPERTY_NUM_ERRORS)) {
		*num_errors = p->num_errors;
	} else {
		OPAE_MSG("No num errors");
		res = FPGA_NOT_FOUND;
	}

	opae_mutex_unlock(err, &p->lock);

	return res;
}

fpga_result __OPAE_API__ fpgaPropertiesSetNumErrors(const fpga_properties prop,
						    uint32_t num_errors)
{
	fpga_result res = FPGA_OK;
	int err;
	struct _fpga_properties *p = opae_validate_and_lock_properties(prop);

	ASSERT_NOT_NULL(p);

	SET_FIELD_VALID(p, FPGA_PROPERTY_NUM_ERRORS);
	p->num_errors = num_errors;

	opae_mutex_unlock(err, &p->lock);

	return res;
}
