/*
 * Acess2 USB Stack
 * - By John Hodge (thePowersGang)
 *
 * usb.c
 * - USB Structure
 */
#define DEBUG	0
#include <acess.h>
#include <vfs.h>
#include <drv_pci.h>
#include "usb.h"

// === IMPORTS ===
extern tUSBHost	*gUSB_Hosts;
extern tUSBDriver gUSBHub_Driver;

// === STRUCTURES ===

// === PROTOTYPES ===
tUSBHub	*USB_RegisterHost(tUSBHostDef *HostDef, void *ControllerPtr, int nPorts);

// === GLOBALS ===
tMutex	glUSB_Hosts;
tUSBHost	*gUSB_Hosts = NULL;
tMutex	glUSB_InterfaceDrivers;
tUSBDriver	*gpUSB_InterfaceDrivers = &gUSBHub_Driver;

// === CODE ===
tUSBHub *USB_RegisterHost(tUSBHostDef *HostDef, void *ControllerPtr, int nPorts)
{
	tUSBHost	*host;
	
	host = malloc(sizeof(tUSBHost) + nPorts*sizeof(tUSBHubPort) + sizeof(tUSBDevice) + sizeof(tUSBInterface));
	if(!host) {
		// Oh, bugger.
		return NULL;
	}
	host->HostDef = HostDef;
	host->Ptr = ControllerPtr;
	memset(host->AddressBitmap, 0, sizeof(host->AddressBitmap));

	host->RootHubDev = (void*)(host->RootHub.Ports + nPorts);
	host->RootHubDev->ParentHub = NULL;
	host->RootHubDev->Host = host;
	host->RootHubDev->Address = 0;
	ASSERT(HostDef->InitControl);
	host->RootHubDev->EndpointHandles[0] = HostDef->InitControl(ControllerPtr, 0, 64);
	host->RootHubDev->nInterfaces = 0;

	host->RootHubIf = (void*)(host->RootHubDev + 1);
	host->RootHubIf->Dev = host->RootHubDev;
	host->RootHubIf->Driver = NULL;
	host->RootHubIf->Data = NULL;
	host->RootHubIf->nEndpoints = 0;

	host->RootHub.Interface = host->RootHubIf;
	host->RootHub.nPorts = nPorts;
	memset(host->RootHub.Ports, 0, sizeof(tUSBHubPort)*nPorts);

	// Append to list
	Mutex_Acquire( &glUSB_Hosts );
	host->Next = gUSB_Hosts;
	gUSB_Hosts = host;
	Mutex_Release( &glUSB_Hosts );

	return &host->RootHub;
}

// --- Drivers ---
void USB_RegisterDriver(tUSBDriver *Driver)
{
	Mutex_Acquire( &glUSB_InterfaceDrivers );
	Driver->Next = gpUSB_InterfaceDrivers;
	gpUSB_InterfaceDrivers = Driver;
	Mutex_Release( &glUSB_InterfaceDrivers );
	
	// TODO: Recheck devices that didn't have a driver
}

tUSBDriver *USB_int_FindDriverByClass(Uint32 ClassCode)
{
	ENTER("xClassCode", ClassCode);
	for( tUSBDriver *ret = gpUSB_InterfaceDrivers; ret; ret = ret->Next )
	{
		LOG(" 0x%x & 0x%x == 0x%x?", ClassCode, ret->Match.Class.ClassMask, ret->Match.Class.ClassCode);
		if( (ClassCode & ret->Match.Class.ClassMask) == ret->Match.Class.ClassCode )
		{
			LOG("Found '%s'", ret->Name);
			LEAVE('p', ret);
			return ret;
		}
	}
	LEAVE('n');
	return NULL;
}

// --- Hub Registration ---
// NOTE: Doesn't do much nowdays
tUSBHub *USB_RegisterHub(tUSBInterface *Device, int PortCount)
{
	tUSBHub	*ret;
	
	ret = malloc(sizeof(tUSBHub) + sizeof(ret->Ports[0])*PortCount);
	ret->Interface = Device;
	ret->nPorts = PortCount;
	memset(ret->Ports, 0, sizeof(ret->Ports[0])*PortCount);
	return ret;
}

void USB_RemoveHub(tUSBHub *Hub)
{
	for( int i = 0; i < Hub->nPorts; i ++ )
	{
		if( Hub->Ports[i].Dev )
		{
			USB_DeviceDisconnected( Hub, i );
		}
	}
	free(Hub);
}

