/*
 *  nvram.c
 *  Region X
 *
 */

#include "nvram.h"

#import <IOKit/IOKitKeys.h>								// for kIONVRAMDeletePropertyKey 


// value = GetOFVariable(name)
//
//   Get a firmware variable by its name converted to the given type. Return NULL if not found
CFDataRef GetOFVariable(char *name)
{
kern_return_t result;
mach_port_t masterPort;
io_registry_entry_t OptionsRef;
CFStringRef nameRef;
CFTypeRef valueRef;

result = IOMasterPort(MACH_PORT_NULL, &masterPort);
if (result != KERN_SUCCESS) return NULL;

OptionsRef = IORegistryEntryFromPath(masterPort, "IODeviceTree:/options");
if (!OptionsRef) return NULL;

nameRef = CFStringCreateWithCString(kCFAllocatorDefault, name, kCFStringEncodingUTF8);
if (!nameRef) return NULL;

valueRef = IORegistryEntryCreateCFProperty(OptionsRef, nameRef, 0, 0);

CFRelease(nameRef);
IOObjectRelease(OptionsRef);

return valueRef;
}



// SetOFVariable(name, value)
//
//   Set or create a firmware variable with name and value.
void SetOFVariable(char *name, char *value)
{
kern_return_t result;
mach_port_t masterPort;
io_registry_entry_t OptionsRef;
CFStringRef nameRef;
CFTypeRef valueRef;
CFTypeID typeID;

result = IOMasterPort(MACH_PORT_NULL, &masterPort);
if (result != KERN_SUCCESS) return;

OptionsRef = IORegistryEntryFromPath(masterPort, "IODeviceTree:/options");
if (!OptionsRef) return;

nameRef = CFStringCreateWithCString(kCFAllocatorDefault, name, kCFStringEncodingUTF8);
if (!nameRef) return;

valueRef = IORegistryEntryCreateCFProperty(OptionsRef, nameRef, 0, 0);
if (valueRef)
	{
	typeID = CFGetTypeID(valueRef);
	CFRelease(valueRef);
	valueRef = ConvertValueToCFTypeRef(typeID, value);
	if (valueRef != 0) result = IORegistryEntrySetCFProperty(OptionsRef, nameRef, valueRef);
	}
else
	{
	while (1)
		{
		// In the default case, try data, string, number, then boolean.    

		valueRef = ConvertValueToCFTypeRef(CFDataGetTypeID(), value);
		if (valueRef != 0)
			{
			result = IORegistryEntrySetCFProperty(OptionsRef, nameRef, valueRef);
			if (result == KERN_SUCCESS) break;
			}

		valueRef = ConvertValueToCFTypeRef(CFStringGetTypeID(), value);
		if (valueRef != 0)
			{
			result = IORegistryEntrySetCFProperty(OptionsRef, nameRef, valueRef);
			if (result == KERN_SUCCESS) break;
			}

		valueRef = ConvertValueToCFTypeRef(CFNumberGetTypeID(), value);
		if (valueRef != 0)
			{
			result = IORegistryEntrySetCFProperty(OptionsRef, nameRef, valueRef);
			if (result == KERN_SUCCESS) break;
			}

		valueRef = ConvertValueToCFTypeRef(CFBooleanGetTypeID(), value);
		if (valueRef != 0)
			{
			result = IORegistryEntrySetCFProperty(OptionsRef, nameRef, valueRef);
			if (result == KERN_SUCCESS) break;
			}
		break;
		}
	}

CFRelease(nameRef);
IOObjectRelease(OptionsRef);
}



// DeleteOFVariable(name)
//
//   Delete the named firmware variable.
void DeleteOFVariable(char *name)
{
SetOFVariable(kIONVRAMDeletePropertyKey, name);
}



// ConvertValueToCFTypeRef(typeID, value)
//
//   Convert the value into a CFType given the typeID.
CFTypeRef ConvertValueToCFTypeRef(CFTypeID typeID, char *value)
{
CFTypeRef     valueRef = 0;
long          length;
unsigned long number;

if (typeID == CFBooleanGetTypeID())
	{
	if (!strcmp("true", value)) valueRef = kCFBooleanTrue;
	else if (!strcmp("TRUE", value)) valueRef = kCFBooleanTrue;
	else if (!strcmp("yes", value)) valueRef = kCFBooleanTrue;
	else if (!strcmp("YES", value)) valueRef = kCFBooleanTrue;
	else if (!strcmp("false", value)) valueRef = kCFBooleanFalse;
	else if (!strcmp("FALSE", value)) valueRef = kCFBooleanFalse;
	else if (!strcmp("no", value)) valueRef = kCFBooleanFalse;
	else if (!strcmp("NO", value)) valueRef = kCFBooleanFalse;
	}
else if (typeID == CFNumberGetTypeID())
	{
	number = strtol(value, 0, 0);
	valueRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &number);
	}
else if (typeID == CFStringGetTypeID())
	{
	valueRef = CFStringCreateWithCString(kCFAllocatorDefault, value, kCFStringEncodingUTF8);
	}
else if (typeID == CFDataGetTypeID())
	{
	length = strlen(value);
	valueRef = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const UInt8 *)value, length, kCFAllocatorDefault);
	}
else return NULL;
  
return valueRef;
}



