/*
 * Copyright 2008-2010, Axel Dörfler, axeld@pinc-software.de.
 * Copyright 2004-2006, Rudolf Cornelissen. All rights reserved.
 *
 * Distributed under the terms of the MIT License.
 */
 
// TODO: rethink the AGP interface for more than one bridge/device!
//	(should be done with the new driver API then)
 
/*
	Notes:
	- currently we just setup all found devices with AGP interface to the same
	highest common mode, we don't distinquish different AGP busses.
	TODO: it might be a better idea to just setup one instead.
 
	- AGP3 defines 'asynchronous request size' and 'calibration cycle' fields
	in the status and command registers. Currently programming zero's which will
	make it work, although further optimisation is possible.
 
	- AGP3.5 also defines isochronous transfers which are not implemented here:
	the hardware keeps them disabled by default.
*/
 
 
#include <AGP.h>
 
#include <stdlib.h>
 
#include <KernelExport.h>
#include <PCI.h>
 
#include <util/OpenHashTable.h>
#include <kernel/lock.h>
#include <vm/vm_page.h>
#include <vm/vm_types.h>
 
#include <lock.h>
 
 
#define TRACE_AGP
#ifdef TRACE_AGP
#	define TRACE(x...) dprintf("\33[36mAGP:\33[0m " x)
#else
#	define TRACE(x...) ;
#endif
#define ERROR(x...) dprintf("\33[36mAGP:\33[0m " x)
 
 
#define MAX_DEVICES	  8
 
#define AGP_ID(address) (address)
#define AGP_STATUS(address) (address + 4)
#define AGP_COMMAND(address) (address + 8)
 
/* read and write to PCI config space */
#define get_pci_config(info, offset, size) \
	(sPCI->read_pci_config((info).bus, (info).device, (info).function, \
		(offset), (size)))
#define set_pci_config(info, offset, size, value) \
	(sPCI->write_pci_config((info).bus, (info).device, (info).function, \
		(offset), (size), (value)))
 
#define RESERVED_APERTURE			0x80000000
#define ALLOCATED_APERTURE			0x40000000
#define BIND_APERTURE				0x20000000
#define APERTURE_PUBLIC_FLAGS_MASK	0x0000ffff
 
struct aperture_memory {
	aperture_memory *next;
	aperture_memory *hash_link;
	addr_t		base;
	size_t		size;
	uint32		flags;
#if !defined(GART_TEST)
	union {
		vm_page	**pages;
		vm_page *page;
	};
#ifdef DEBUG_PAGE_ACCESS
	thread_id	allocating_thread;
#endif
#else
	area_id		area;
#endif
};
 
class Aperture;
 
class MemoryHashDefinition {
public:
	typedef addr_t KeyType;
	typedef aperture_memory ValueType;
 
	MemoryHashDefinition(aperture_info &info) : fInfo(info) {}
 
	size_t HashKey(const KeyType &base) const
		{ return (base - fInfo.base) / B_PAGE_SIZE; }
	size_t Hash(aperture_memory *memory) const
		{ return (memory->base - fInfo.base) / B_PAGE_SIZE; }
	bool Compare(const KeyType &base, aperture_memory *memory) const
		{ return base == memory->base; }
	aperture_memory *&GetLink(aperture_memory *memory) const
		{ return memory->hash_link; }
 
private:
	aperture_info	&fInfo;
};
 
typedef BOpenHashTable<MemoryHashDefinition> MemoryHashTable;
 
struct agp_device_info {
	uint8		address;	/* location of AGP interface in PCI capabilities */
	agp_info	info;
};
 
class Aperture {
public:
	Aperture(agp_gart_bus_module_info *module, void *aperture);
	~Aperture();
 
	status_t InitCheck() const { return fLock.sem >= B_OK ? B_OK : fLock.sem; }
 
	void DeleteMemory(aperture_memory *memory);
	aperture_memory *CreateMemory(size_t size, size_t alignment, uint32 flags);
 
	status_t AllocateMemory(aperture_memory *memory, uint32 flags);
 
	status_t UnbindMemory(aperture_memory *memory);
	status_t BindMemory(aperture_memory *memory, addr_t base, size_t size);
 
	status_t GetInfo(aperture_info *info);
 
	aperture_memory *GetMemory(addr_t base) { return fHashTable.Lookup(base); }
 
	addr_t Base() const { return fInfo.base; }
	addr_t Size() const { return fInfo.size; }
	int32 ID() const { return fID; }
	struct lock &Lock() { return fLock; }
 
private:
	bool _AdaptToReserved(addr_t &base, size_t &size, int32 *_offset = NULL);
	void _Free(aperture_memory *memory);
	void _Remove(aperture_memory *memory);
	status_t _Insert(aperture_memory *memory, size_t size, size_t alignment,
		uint32 flags);
 
	struct lock					fLock;
	agp_gart_bus_module_info	*fModule;
	int32						fID;
	aperture_info				fInfo;
	MemoryHashTable				fHashTable;
	aperture_memory				*fFirstMemory;
	void						*fPrivateAperture;
 
public:
	Aperture					*fNext;
};
 
class ApertureHashDefinition {
public:
	typedef int32 KeyType;
	typedef Aperture ValueType;
 
	size_t HashKey(const KeyType &id) const
		{ return id; }
	size_t Hash(Aperture *aperture) const
		{ return aperture->ID(); }
	bool Compare(const KeyType &id, Aperture *aperture) const
		{ return id == aperture->ID(); }
	Aperture *&GetLink(Aperture *aperture) const
		{ return aperture->fNext; }
};
 
typedef BOpenHashTable<ApertureHashDefinition> ApertureHashTable;
 
 
static agp_device_info sDeviceInfos[MAX_DEVICES];
static uint32 sDeviceCount;
static pci_module_info *sPCI;
static int32 sAcquired;
static ApertureHashTable sApertureHashTable;
static int32 sNextApertureID;
static struct lock sLock;
 
 
//	#pragma mark - private support functions
 
 
/*!	Makes sure that all bits lower than the maximum supported rate is set. */
static uint32
fix_rate_support(uint32 command)
{
	if ((command & AGP_3_MODE) != 0) {
		if ((command & AGP_3_8x) != 0)
			command |= AGP_3_4x;
 
		command &= ~AGP_RATE_MASK | AGP_3_8x | AGP_3_4x;
		command |= AGP_SBA;
			// SBA is required for AGP3
	} else {
		/* AGP 2.0 scheme applies */
		if ((command & AGP_2_4x) != 0)
			command |= AGP_2_2x;
		if ((command & AGP_2_2x) != 0)
			command |= AGP_2_1x;
	}
 
	return command;
}
 
 
/*!	Makes sure that only the highest rate bit is set. */
static uint32
fix_rate_command(uint32 command)
{
	if ((command & AGP_3_MODE) != 0) {
		if ((command & AGP_3_8x) != 0)
			command &= ~AGP_3_4x;
	} else {
		/* AGP 2.0 scheme applies */
		if ((command & AGP_2_4x) != 0)
			command &= ~(AGP_2_2x | AGP_2_1x);
		if ((command & AGP_2_2x) != 0)
			command &= ~AGP_2_1x;
	}
 
	return command;
}
 
 
/*!	Checks the capabilities of the device, and removes everything from
	\a command that the device does not support.
*/
static void
check_capabilities(agp_device_info &deviceInfo, uint32 &command)
{
	uint32 agpStatus = deviceInfo.info.interface.status;
	if (deviceInfo.info.class_base == PCI_bridge) {
		// make sure the AGP rate support mask is correct
		// (ie. has the lower bits set)
		agpStatus = fix_rate_support(agpStatus);
	}
 
	TRACE("device %u.%u.%u has AGP capabilities %" B_PRIx32 "\n", deviceInfo.info.bus,
		deviceInfo.info.device, deviceInfo.info.function, agpStatus);
 
	// block non-supported AGP modes
	command &= (agpStatus & (AGP_3_MODE | AGP_RATE_MASK))
		| ~(AGP_3_MODE | AGP_RATE_MASK);
 
	// If no AGP mode is supported at all, nothing remains:
	// devices exist that have the AGP style connector with AGP style registers,
	// but not the features!
	// (confirmed Matrox Millenium II AGP for instance)
	if ((agpStatus & AGP_RATE_MASK) == 0)
		command = 0;
 
	// block side band adressing if not supported
	if ((agpStatus & AGP_SBA) == 0)
		command &= ~AGP_SBA;
 
	// block fast writes if not supported
	if ((agpStatus & AGP_FAST_WRITE) == 0)
		command &= ~AGP_FAST_WRITE;
 
	// adjust maximum request depth to least depth supported
	// note: this is writable only in the graphics card
	uint8 requestDepth = ((agpStatus & AGP_REQUEST) >> AGP_REQUEST_SHIFT);
	if (requestDepth < ((command & AGP_REQUEST) >> AGP_REQUEST_SHIFT)) {
		command &= ~AGP_REQUEST;
		command |= (requestDepth << AGP_REQUEST_SHIFT);
	}
}
 
 
/*!	Checks the PCI capabilities if the device is an AGP device
*/
static bool
is_agp_device(pci_info &info, uint8 *_address)
{
	// Check if device implements a list of capabilities
	if ((get_pci_config(info, PCI_status, 2) & PCI_status_capabilities) == 0)
		return false;
 
	// Get pointer to PCI capabilities list
	// (AGP devices only, no need to take cardbus into account)
	uint8 address = get_pci_config(info, PCI_capabilities_ptr, 1);
 
	while (true) {
		uint8 id = get_pci_config(info, address, 1);
		uint8 next = get_pci_config(info, address + 1, 1) & ~0x3;
 
		if (id == PCI_cap_id_agp) {
			// is an AGP device
			if (_address != NULL)
				*_address = address;
			return true;
		}
		if (next == 0) {
			// end of list
			break;
		}
 
		address = next;
	}
 
	return false;
}
 
 
static status_t
get_next_agp_device(uint32 *_cookie, pci_info &info, agp_device_info &device)
{
	uint32 index = *_cookie;
 
	// find devices
 
	for (; sPCI->get_nth_pci_info(index, &info) == B_OK; index++) {
		// is it a bridge or a graphics card?
		if ((info.class_base != PCI_bridge || info.class_sub != PCI_host)
			&& info.class_base != PCI_display)
			continue;
 
		if (is_agp_device(info, &device.address)) {
			device.info.vendor_id = info.vendor_id;
			device.info.device_id = info.device_id;
			device.info.bus = info.bus;
			device.info.device = info.device;
			device.info.function = info.function;
			device.info.class_sub = info.class_sub;
			device.info.class_base = info.class_base;
 
			/* get the contents of the AGP registers from this device */
			device.info.interface.capability_id = get_pci_config(info,
				AGP_ID(device.address), 4);
			device.info.interface.status = get_pci_config(info,
				AGP_STATUS(device.address), 4);
			device.info.interface.command = get_pci_config(info,
				AGP_COMMAND(device.address), 4);
 
			*_cookie = index + 1;
			return B_OK;
		}
	}
 
	return B_ENTRY_NOT_FOUND;
}
 
 
static void
set_agp_command(agp_device_info &deviceInfo, uint32 command)
{
	set_pci_config(deviceInfo.info, AGP_COMMAND(deviceInfo.address), 4, command);
	deviceInfo.info.interface.command = get_pci_config(deviceInfo.info,
		AGP_COMMAND(deviceInfo.address), 4);
}
 
 
static void
set_pci_mode()
{
	TRACE("set PCI mode on all AGP capable devices.\n");
 
	// First program all graphics cards
 
	for (uint32 index = 0; index < sDeviceCount; index++) {
		agp_device_info &deviceInfo = sDeviceInfos[index];
		if (deviceInfo.info.class_base != PCI_display)
			continue;
 
		set_agp_command(deviceInfo, 0);
	}
 
	// Then program all bridges - it's the other around for AGP mode
 
	for (uint32 index = 0; index < sDeviceCount; index++) {
		agp_device_info &deviceInfo = sDeviceInfos[index];
		if (deviceInfo.info.class_base != PCI_bridge)
			continue;
 
		set_agp_command(deviceInfo, 0);
	}
 
	// Wait 10mS for the bridges to recover (failsafe!)
	// Note: some SiS bridge chipsets apparantly require 5mS to recover
	// or the master (graphics card) cannot be initialized correctly!
	snooze(10000);
}
 
 
status_t
get_area_base_and_size(area_id area, addr_t &base, size_t &size)
{
	area_info info;
	status_t status = get_area_info(area, &info);
	if (status < B_OK)
		return status;
 
	base = (addr_t)info.address;
	size = info.size;
	return B_OK;
}
 
 
Aperture *
get_aperture(aperture_id id)
{
	Autolock _(sLock);
	return sApertureHashTable.Lookup(id);
}
 
 
//	#pragma mark - Aperture
 
 
Aperture::Aperture(agp_gart_bus_module_info *module, void *aperture)
	:
	fModule(module),
	fHashTable(fInfo),
	fFirstMemory(NULL),
	fPrivateAperture(aperture)
{
	fModule->get_aperture_info(fPrivateAperture, &fInfo);
	fID = atomic_add(&sNextApertureID, 1);
	init_lock(&fLock, "aperture");
}
 
 
Aperture::~Aperture()
{
	while (fFirstMemory != NULL) {
		DeleteMemory(fFirstMemory);
	}
 
	fModule->delete_aperture(fPrivateAperture);
	put_module(fModule->info.name);
}
 
 
status_t
Aperture::GetInfo(aperture_info *info)
{
	if (info == NULL)
		return B_BAD_VALUE;
 
	*info = fInfo;
	return B_OK;
}
 
 
void
Aperture::DeleteMemory(aperture_memory *memory)
{
	TRACE("delete memory %p\n", memory);
 
	UnbindMemory(memory);
	_Free(memory);
	_Remove(memory);
	fHashTable.Remove(memory);
	delete memory;
}
 
 
aperture_memory *
Aperture::CreateMemory(size_t size, size_t alignment, uint32 flags)
{
	aperture_memory *memory = new(std::nothrow) aperture_memory;
	if (memory == NULL)
		return NULL;
 
	status_t status = _Insert(memory, size, alignment, flags);
	if (status < B_OK) {
		ERROR("Aperture::CreateMemory(): did not find a free space large for "
			"this memory object\n");
		delete memory;
		return NULL;
	}
 
	TRACE("create memory %p, base %" B_PRIxADDR ", size %" B_PRIxSIZE
		", flags %" B_PRIx32 "\n", memory, memory->base, memory->size, flags);
 
	memory->flags = flags;
#if !defined(GART_TEST)
	memory->pages = NULL;
#else
	memory->area = -1;
#endif
 
	fHashTable.Insert(memory);
	return memory;
}
 
 
bool
Aperture::_AdaptToReserved(addr_t &base, size_t &size, int32 *_offset)
{
	addr_t reservedEnd = fInfo.base + fInfo.reserved_size;
	if (reservedEnd <= base)
		return false;
 
	if (reservedEnd >= base + size) {
		size = 0;
		return true;
	}
 
	if (_offset != NULL)
		*_offset = reservedEnd - base;
 
	size -= reservedEnd - base;
	base = reservedEnd;
	return true;
}
 
 
status_t
Aperture::AllocateMemory(aperture_memory *memory, uint32 flags)
{
	// We don't need to allocate reserved memory - it's
	// already there for us to use
	addr_t base = memory->base;
	size_t size = memory->size;
	if (_AdaptToReserved(base, size)) {
		if (size == 0) {
			TRACE("allocation is made of reserved memory\n");
			return B_OK;
		}
 
		memset((void *)memory->base, 0, memory->size - size);
	}
	TRACE("allocate %ld bytes out of %ld\n", size, memory->size);
 
#if !defined(GART_TEST)
	uint32 count = size / B_PAGE_SIZE;
 
	if ((flags & B_APERTURE_NEED_PHYSICAL) != 0) {
		physical_address_restrictions restrictions = {};
#if B_HAIKU_PHYSICAL_BITS > 32
		restrictions.high_address = (phys_addr_t)1 << 32;
			// TODO: Work-around until intel_gart can deal with physical
			// addresses > 4 GB.
#endif
		memory->page = vm_page_allocate_page_run(
			PAGE_STATE_WIRED | VM_PAGE_ALLOC_CLEAR, count, &restrictions,
			VM_PRIORITY_SYSTEM);
		if (memory->page == NULL) {
			ERROR("Aperture::AllocateMemory(): vm_page_allocate_page_run() "
				"failed (with B_APERTURE_NEED_PHYSICAL)\n");
			return B_NO_MEMORY;
		}
	} else {
		// Allocate table to hold the pages
		memory->pages = (vm_page **)malloc(count * sizeof(vm_page *));
		if (memory->pages == NULL)
			return B_NO_MEMORY;
 
#if B_HAIKU_PHYSICAL_BITS > 32
		// TODO: Work-around until intel_gart can deal with physical
		// addresses > 4 GB.
		physical_address_restrictions restrictions = {};
		restrictions.high_address = (phys_addr_t)1 << 32;
		vm_page* page = vm_page_allocate_page_run(
			PAGE_STATE_WIRED | VM_PAGE_ALLOC_CLEAR, count, &restrictions,
			VM_PRIORITY_SYSTEM);
		if (page == NULL) {
			ERROR("Aperture::AllocateMemory(): vm_page_allocate_page_run() "
				"failed (without B_APERTURE_NEED_PHYSICAL)\n");
			return B_NO_MEMORY;
		}
 
		for (uint32 i = 0; i < count; i++)
			memory->pages[i] = page + i;
#else
		vm_page_reservation reservation;
		vm_page_reserve_pages(&reservation, count, VM_PRIORITY_SYSTEM);
		for (uint32 i = 0; i < count; i++) {
			memory->pages[i] = vm_page_allocate_page(&reservation,
				PAGE_STATE_WIRED | VM_PAGE_ALLOC_CLEAR);
		}
		vm_page_unreserve_pages(&reservation);
#endif
	}
 
#ifdef DEBUG_PAGE_ACCESS
	memory->allocating_thread = find_thread(NULL);
#endif
 
#else	// GART_TEST
	void *address;
	memory->area = create_area("GART memory", &address, B_ANY_KERNEL_ADDRESS,
		size, B_FULL_LOCK | ((flags & B_APERTURE_NEED_PHYSICAL) != 0
			? B_CONTIGUOUS : 0), 0);
	if (memory->area < B_OK) {
		ERROR("Aperture::AllocateMemory(): create_area() failed\n");
		return B_NO_MEMORY;
	}
#endif
 
	memory->flags |= ALLOCATED_APERTURE;
	return B_OK;
}
 
 
status_t
Aperture::UnbindMemory(aperture_memory *memory)
{
	if ((memory->flags & BIND_APERTURE) == 0)
		return B_BAD_VALUE;
 
	// We must not unbind reserved memory
	addr_t base = memory->base;
	size_t size = memory->size;
	if (_AdaptToReserved(base, size) && size == 0) {
		memory->flags &= ~BIND_APERTURE;
		return B_OK;
	}
 
	addr_t start = base - Base();
	TRACE("unbind %ld bytes at %lx\n", size, start);
 
	for (addr_t offset = 0; offset < memory->size; offset += B_PAGE_SIZE) {
		status_t status = fModule->unbind_page(fPrivateAperture, start + offset);
		if (status < B_OK)
			return status;
	}
 
	memory->flags &= ~BIND_APERTURE;
	fModule->flush_tlbs(fPrivateAperture);
	return B_OK;
}
 
 
status_t
Aperture::BindMemory(aperture_memory *memory, addr_t address, size_t size)
{
	bool physical = false;
 
	if ((memory->flags & ALLOCATED_APERTURE) != 0) {
		// We allocated this memory, get the base and size from there
		size = memory->size;
		physical = true;
	}
 
	// We don't need to bind reserved memory
	addr_t base = memory->base;
	int32 offset;
	if (_AdaptToReserved(base, size, &offset)) {
		if (size == 0) {
			TRACE("reserved memory already bound\n");
			memory->flags |= BIND_APERTURE;
			return B_OK;
		}
 
		address += offset;
	}
 
	addr_t start = base - Base();
	TRACE("bind %ld bytes at %lx\n", size, base);
 
	for (addr_t offset = 0; offset < size; offset += B_PAGE_SIZE) {
		phys_addr_t physicalAddress = 0;
		status_t status;
 
		if (!physical) {
			physical_entry entry;
			status = get_memory_map((void *)(address + offset), B_PAGE_SIZE,
				&entry, 1);
			if (status < B_OK) {
				ERROR("Aperture::BindMemory(): get_memory_map() failed\n");
				return status;
			}
 
			physicalAddress = entry.address;
		} else {
			uint32 index = offset >> PAGE_SHIFT;
			vm_page *page;
			if ((memory->flags & B_APERTURE_NEED_PHYSICAL) != 0)
				page = memory->page + index;
			else
				page = memory->pages[index];
 
			physicalAddress
				= (phys_addr_t)page->physical_page_number << PAGE_SHIFT;
		}
 
		status = fModule->bind_page(fPrivateAperture, start + offset,
			physicalAddress);
		if (status < B_OK) {
			ERROR("Aperture::BindMemory(): bind_page() failed\n");
			return status;
		}
	}
 
	memory->flags |= BIND_APERTURE;
	fModule->flush_tlbs(fPrivateAperture);
	return B_OK;
}
 
 
void
Aperture::_Free(aperture_memory *memory)
{
	if ((memory->flags & ALLOCATED_APERTURE) == 0)
		return;
 
#if !defined(GART_TEST)
	// Remove the stolen area from the allocation
	size_t size = memory->size;
	addr_t reservedEnd = fInfo.base + fInfo.reserved_size;
	if (memory->base < reservedEnd)
		size -= reservedEnd - memory->base;
 
	// Free previously allocated pages and page table
	uint32 count = size / B_PAGE_SIZE;
 
	if ((memory->flags & B_APERTURE_NEED_PHYSICAL) != 0) {
		vm_page *page = memory->page;
		for (uint32 i = 0; i < count; i++, page++) {
			DEBUG_PAGE_ACCESS_TRANSFER(page, memory->allocating_thread);
			vm_page_set_state(page, PAGE_STATE_FREE);
		}
 
		memory->page = NULL;
	} else {
		for (uint32 i = 0; i < count; i++) {
			DEBUG_PAGE_ACCESS_TRANSFER(memory->pages[i],
				memory->allocating_thread);
			vm_page_set_state(memory->pages[i], PAGE_STATE_FREE);
		}
 
		free(memory->pages);
		memory->pages = NULL;
	}
#else
	delete_area(memory->area);
	memory->area = -1;
#endif
 
	memory->flags &= ~ALLOCATED_APERTURE;
}
 
 
void
Aperture::_Remove(aperture_memory *memory)
{
	aperture_memory *current = fFirstMemory, *last = NULL;
 
	while (current != NULL) {
		if (memory == current) {
			if (last != NULL) {
				last->next = current->next;
			} else {
				fFirstMemory = current->next;
			}
			break;
		}
 
		last = current;
		current = current->next;
	}
}
 
 
status_t
Aperture::_Insert(aperture_memory *memory, size_t size, size_t alignment,
	uint32 flags)
{
	aperture_memory *last = NULL;
	aperture_memory *next;
	bool foundSpot = false;
 
	// do some sanity checking
	if (size == 0 || size > fInfo.size)
		return B_BAD_VALUE;
 
	if (alignment < B_PAGE_SIZE)
		alignment = B_PAGE_SIZE;
 
	addr_t start = fInfo.base;
	if ((flags & (B_APERTURE_NON_RESERVED | B_APERTURE_NEED_PHYSICAL)) != 0)
		start += fInfo.reserved_size;
 
	start = ROUNDUP(start, alignment);
	if (start > fInfo.base - 1 + fInfo.size || start < fInfo.base)
		return B_NO_MEMORY;
 
	// walk up to the spot where we should start searching
 
	next = fFirstMemory;
	while (next) {
		if (next->base >= start + size) {
			// we have a winner
			break;
		}
		last = next;
		next = next->next;
	}
 
	// find a big enough hole
	if (last == NULL) {
		// see if we can build it at the beginning of the virtual map
		if (next == NULL || (next->base >= ROUNDUP(start, alignment) + size)) {
			memory->base = ROUNDUP(start, alignment);
			foundSpot = true;
		} else {
			last = next;
			next = next->next;
		}
	}
 
	if (!foundSpot) {
		// keep walking
		while (next != NULL) {
			if (next->base >= ROUNDUP(last->base + last->size, alignment) + size) {
				// we found a spot (it'll be filled up below)
				break;
			}
			last = next;
			next = next->next;
		}
 
		if ((fInfo.base + (fInfo.size - 1)) >= (ROUNDUP(last->base + last->size,
				alignment) + (size - 1))) {
			// got a spot
			foundSpot = true;
			memory->base = ROUNDUP(last->base + last->size, alignment);
			if (memory->base < start)
				memory->base = start;
		}
 
		if (!foundSpot)
			return B_NO_MEMORY;
	}
 
	memory->size = size;
	if (last) {
		memory->next = last->next;
		last->next = memory;
	} else {
		memory->next = fFirstMemory;
		fFirstMemory = memory;
	}
 
	return B_OK;
}
 
 
//	#pragma mark - AGP module interface
 
 
status_t
get_nth_agp_info(uint32 index, agp_info *info)
{
	TRACE("get_nth_agp_info(index %" B_PRIu32 ")\n", index);
 
	if (index >= sDeviceCount)
		return B_BAD_VALUE;
 
	// refresh from the contents of the AGP registers from this device
	sDeviceInfos[index].info.interface.status = get_pci_config(
		sDeviceInfos[index].info, AGP_STATUS(sDeviceInfos[index].address), 4);
	sDeviceInfos[index].info.interface.command = get_pci_config(
		sDeviceInfos[index].info, AGP_COMMAND(sDeviceInfos[index].address), 4);
 
	*info = sDeviceInfos[index].info;
	return B_OK;
}
 
 
status_t
acquire_agp(void)
{
	if (atomic_or(&sAcquired, 1) == 1)
		return B_BUSY;
 
	return B_OK;
}
 
 
void
release_agp(void)
{
	atomic_and(&sAcquired, 0);
}
 
 
uint32
set_agp_mode(uint32 command)
{
	TRACE("set_agp_mode(command %" B_PRIx32 ")\n", command);
 
	if ((command & AGP_ENABLE) == 0) {
		set_pci_mode();
		return 0;
	}
 
	// Make sure we accept all modes lower than requested one and we
	// reset reserved bits
	command = fix_rate_support(command);
 
	// iterate through our device list to find the common capabilities supported
	for (uint32 index = 0; index < sDeviceCount; index++) {
		agp_device_info &deviceInfo = sDeviceInfos[index];
 
		// Refresh from the contents of the AGP capability registers
		// (note: some graphics driver may have been tweaking, like nvidia)
		deviceInfo.info.interface.status = get_pci_config(deviceInfo.info,
			AGP_STATUS(deviceInfo.address), 4);
 
		check_capabilities(deviceInfo, command);
	}
 
	command = fix_rate_command(command);
	TRACE("set AGP command %" B_PRIx32 " on all capable devices.\n", command);
 
	// The order of programming differs for enabling/disabling AGP mode
	// (see AGP specification)
 
	// First program all bridges (master)
 
	for (uint32 index = 0; index < sDeviceCount; index++) {
		agp_device_info &deviceInfo = sDeviceInfos[index];
		if (deviceInfo.info.class_base != PCI_bridge)
			continue;
 
		set_agp_command(deviceInfo, command);
	}
 
	// Wait 10mS for the bridges to recover (failsafe, see set_pci_mode()!)
	snooze(10000);
 
	// Then all graphics cards (target)
 
	for (uint32 index = 0; index < sDeviceCount; index++) {
		agp_device_info &deviceInfo = sDeviceInfos[index];
		if (deviceInfo.info.class_base != PCI_display)
			continue;
 
		set_agp_command(deviceInfo, command);
	}
 
	return command;
}
 
 
//	#pragma mark - GART module interface
 
 
static aperture_id
map_aperture(uint8 bus, uint8 device, uint8 function, size_t size,
	addr_t *_apertureBase)
{
	void *iterator = open_module_list("busses/agp_gart");
	status_t status = B_ENTRY_NOT_FOUND;
	Aperture *aperture = NULL;
 
	Autolock _(sLock);
 
	while (true) {
		char name[256];
		size_t nameLength = sizeof(name);
		if (read_next_module_name(iterator, name, &nameLength) != B_OK)
			break;
 
		agp_gart_bus_module_info *module;
		if (get_module(name, (module_info **)&module) == B_OK) {
			void *privateAperture;
			status = module->create_aperture(bus, device, function, size,
				&privateAperture);
			if (status < B_OK) {
				put_module(name);
				continue;
			}
 
			aperture = new(std::nothrow) Aperture(module, privateAperture);
			status = aperture->InitCheck();
			if (status == B_OK) {
				if (_apertureBase != NULL)
					*_apertureBase = aperture->Base();
 
				sApertureHashTable.Insert(aperture);
			} else {
				delete aperture;
				aperture = NULL;
			}
			break;
		}
	}
 
	close_module_list(iterator);
	return aperture != NULL ? aperture->ID() : status;
}
 
 
static aperture_id
map_custom_aperture(gart_bus_module_info *module, addr_t *_apertureBase)
{
	return B_ERROR;
}
 
 
static status_t
unmap_aperture(aperture_id id)
{
	Autolock _(sLock);
	Aperture *aperture = sApertureHashTable.Lookup(id);
	if (aperture == NULL)
		return B_ENTRY_NOT_FOUND;
 
	sApertureHashTable.Remove(aperture);
	delete aperture;
	return B_OK;
}
 
 
static status_t
get_aperture_info(aperture_id id, aperture_info *info)
{
	Aperture *aperture = get_aperture(id);
	if (aperture == NULL)
		return B_ENTRY_NOT_FOUND;
 
	Autolock _(aperture->Lock());
	return aperture->GetInfo(info);
}
 
 
static status_t
allocate_memory(aperture_id id, size_t size, size_t alignment, uint32 flags,
	addr_t *_apertureBase, phys_addr_t *_physicalBase)
{
	if ((flags & ~APERTURE_PUBLIC_FLAGS_MASK) != 0 || _apertureBase == NULL)
		return B_BAD_VALUE;
 
	Aperture *aperture = get_aperture(id);
	if (aperture == NULL)
		return B_ENTRY_NOT_FOUND;
 
	size = ROUNDUP(size, B_PAGE_SIZE);
 
	Autolock _(aperture->Lock());
 
	aperture_memory *memory = aperture->CreateMemory(size, alignment, flags);
	if (memory == NULL)
		return B_NO_MEMORY;
 
	status_t status = aperture->AllocateMemory(memory, flags);
	if (status == B_OK)
		status = aperture->BindMemory(memory, 0, 0);
	if (status < B_OK) {
		aperture->DeleteMemory(memory);
		return status;
	}
 
	if (_physicalBase != NULL && (flags & B_APERTURE_NEED_PHYSICAL) != 0) {
#if !defined(GART_TEST)
		*_physicalBase
			= (phys_addr_t)memory->page->physical_page_number * B_PAGE_SIZE;
#else
		physical_entry entry;
		status = get_memory_map((void *)memory->base, B_PAGE_SIZE, &entry, 1);
		if (status < B_OK) {
			aperture->DeleteMemory(memory);
			return status;
		}
 
		*_physicalBase = entry.address;
#endif
	}
 
	*_apertureBase = memory->base;
	return B_OK;
}
 
 
static status_t
free_memory(aperture_id id, addr_t base)
{
	Aperture *aperture = get_aperture(id);
	if (aperture == NULL)
		return B_ENTRY_NOT_FOUND;
 
	Autolock _(aperture->Lock());
	aperture_memory *memory = aperture->GetMemory(base);
	if (memory == NULL)
		return B_BAD_VALUE;
 
	aperture->DeleteMemory(memory);
	return B_OK;
}
 
 
static status_t
reserve_aperture(aperture_id id, size_t size, addr_t *_apertureBase)
{
	Aperture *aperture = get_aperture(id);
	if (aperture == NULL)
		return B_ENTRY_NOT_FOUND;
 
	return B_ERROR;
}
 
 
static status_t
unreserve_aperture(aperture_id id, addr_t apertureBase)
{
	Aperture *aperture = get_aperture(id);
	if (aperture == NULL)
		return B_ENTRY_NOT_FOUND;
 
	return B_ERROR;
}
 
 
static status_t
bind_aperture(aperture_id id, area_id area, addr_t base, size_t size,
	size_t alignment, addr_t reservedBase, addr_t *_apertureBase)
{
	Aperture *aperture = get_aperture(id);
	if (aperture == NULL)
		return B_ENTRY_NOT_FOUND;
 
	if (area < 0) {
		if (size == 0 || size > aperture->Size()
			|| (base & (B_PAGE_SIZE - 1)) != 0
			|| base == 0)
			return B_BAD_VALUE;
 
		size = ROUNDUP(size, B_PAGE_SIZE);
	}
 
	if (area >= 0) {
		status_t status = get_area_base_and_size(area, base, size);
		if (status < B_OK)
			return status;
	}
 
	Autolock _(aperture->Lock());
	aperture_memory *memory = NULL;
	if (reservedBase != 0) {
		// use reserved aperture to bind the pages
		memory = aperture->GetMemory(reservedBase);
		if (memory == NULL)
			return B_BAD_VALUE;
	} else {
		// create new memory object
		memory = aperture->CreateMemory(size, alignment,
			B_APERTURE_NON_RESERVED);
		if (memory == NULL)
			return B_NO_MEMORY;
	}
 
	// just bind the physical pages backing the memory into the GART
 
	status_t status = aperture->BindMemory(memory, base, size);
	if (status < B_OK) {
		if (reservedBase < 0)
			aperture->DeleteMemory(memory);
 
		return status;
	}
 
	if (_apertureBase != NULL)
		*_apertureBase = memory->base;
 
	return B_OK;
}
 
 
static status_t
unbind_aperture(aperture_id id, addr_t base)
{
	Aperture *aperture = get_aperture(id);
	if (aperture == NULL)
		return B_ENTRY_NOT_FOUND;
 
	Autolock _(aperture->Lock());
	aperture_memory *memory = aperture->GetMemory(base);
	if (memory == NULL || (memory->flags & BIND_APERTURE) == 0)
		return B_BAD_VALUE;
 
	if ((memory->flags & ALLOCATED_APERTURE) != 0)
		panic("unbind memory %lx (%p) allocated by agp_gart.", base, memory);
 
	status_t status = aperture->UnbindMemory(memory);
	if (status < B_OK)
		return status;
 
	if ((memory->flags & RESERVED_APERTURE) == 0)
		aperture->DeleteMemory(memory);
 
	return B_OK;
}
 
 
//	#pragma mark -
 
 
static status_t
agp_init(void)
{
	TRACE("bus manager init\n");
 
	if (get_module(B_PCI_MODULE_NAME, (module_info **)&sPCI) != B_OK)
		return B_ERROR;
 
	uint32 cookie = 0;
	sDeviceCount = 0;
	pci_info info;
	while (get_next_agp_device(&cookie, info, sDeviceInfos[sDeviceCount])
			== B_OK) {
		sDeviceCount++;
	}
 
	TRACE("found %" B_PRId32 " AGP devices\n", sDeviceCount);
 
	// Since there can be custom aperture modules (for memory management only),
	// we always succeed if we could get the resources we need.
 
	new(&sApertureHashTable) ApertureHashTable();
	return init_lock(&sLock, "agp_gart");
}
 
 
void
agp_uninit(void)
{
	TRACE("bus manager uninit\n");
 
	ApertureHashTable::Iterator iterator = sApertureHashTable.GetIterator();
	while (iterator.HasNext()) {
		Aperture *aperture = iterator.Next();
		sApertureHashTable.Remove(aperture);
		delete aperture;
	}
 
	put_module(B_PCI_MODULE_NAME);
}
 
 
static int32
agp_std_ops(int32 op, ...)
{
	switch (op) {
		case B_MODULE_INIT:
			return agp_init();
		case B_MODULE_UNINIT:
			agp_uninit();
			return B_OK;
	}
 
	return B_BAD_VALUE;
}
 
 
static struct agp_gart_module_info sAGPModuleInfo = {
	{
		{
			B_AGP_GART_MODULE_NAME,
			B_KEEP_LOADED,		// Keep loaded, even if no driver requires it
			agp_std_ops
		},
		NULL 					// the rescan function
	},
	get_nth_agp_info,
	acquire_agp,
	release_agp,
	set_agp_mode,
 
	map_aperture,
	map_custom_aperture,
	unmap_aperture,
	get_aperture_info,
	allocate_memory,
	free_memory,
	reserve_aperture,
	unreserve_aperture,
	bind_aperture,
	unbind_aperture,
};
 
module_info *modules[] = {
	(module_info *)&sAGPModuleInfo,
	NULL
};

V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fNext.

V547 Expression 'reservedBase < 0' is always false. Unsigned type value is never < 0.