haiku/src/system/kernel/device_manager/io_resources.cpp
Jérôme Duval 19b07e67d2 when acquiring results in an error, the resource should be reset to avoid a crash on delete
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@25748 a95241bf-73f2-0310-859d-f6bbb57e9c96
2008-06-01 15:27:00 +00:00

153 lines
2.7 KiB
C++

/*
* Copyright 2004-2008, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
* Copyright 2002-2004, Thomas Kurschel. All rights reserved.
*
* Distributed under the terms of the MIT License.
*/
#include "io_resources.h"
#include <stdlib.h>
#include <string.h>
//#define TRACE_IO_RESOURCES
#ifdef TRACE_IO_RESOURCES
# define TRACE(x) dprintf x
#else
# define TRACE(x) ;
#endif
typedef DoublyLinkedList<io_resource_private,
DoublyLinkedListMemberGetLink<io_resource_private,
&io_resource_private::fTypeLink> > ResourceTypeList;
static ResourceTypeList sMemoryList;
static ResourceTypeList sPortList;
static ResourceTypeList sDMAChannelList;
io_resource_private::io_resource_private()
{
_Init();
}
io_resource_private::~io_resource_private()
{
Release();
}
void
io_resource_private::_Init()
{
type = 0;
base = 0;
length = 0;
}
status_t
io_resource_private::Acquire(const io_resource& resource)
{
if (!_IsValid(resource))
return B_BAD_VALUE;
type = resource.type;
base = resource.base;
if (type != B_ISA_DMA_CHANNEL)
length = resource.length;
else
length = 1;
ResourceTypeList* list = NULL;
switch (type) {
case B_IO_MEMORY:
list = &sMemoryList;
break;
case B_IO_PORT:
list = &sPortList;
break;
case B_ISA_DMA_CHANNEL:
list = &sDMAChannelList;
break;
}
ResourceTypeList::Iterator iterator = list->GetIterator();
while (iterator.HasNext()) {
io_resource* resource = iterator.Next();
// we need the "base + length - 1" trick to avoid wrap around at 4 GB
if (resource->base >= base
&& resource->base + length - 1 <= base + length - 1) {
// This range is already covered by someone else
// TODO: we might want to ignore resources that belong to
// a node that isn't used.
_Init();
return B_RESOURCE_UNAVAILABLE;
}
}
list->Add(this);
return B_OK;
}
void
io_resource_private::Release()
{
if (type == 0)
return;
switch (type) {
case B_IO_MEMORY:
sMemoryList.Remove(this);
break;
case B_IO_PORT:
sPortList.Remove(this);
break;
case B_ISA_DMA_CHANNEL:
sDMAChannelList.Remove(this);
break;
}
_Init();
}
/*static*/ bool
io_resource_private::_IsValid(const io_resource& resource)
{
switch (resource.type) {
case B_IO_MEMORY:
return resource.base + resource.length > resource.base;
case B_IO_PORT:
return (uint16)resource.base == resource.base
&& (uint16)resource.length == resource.length
&& resource.base + resource.length > resource.base;
case B_ISA_DMA_CHANNEL:
return resource.base <= 8;
default:
return false;
}
}
// #pragma mark -
void
dm_init_io_resources(void)
{
new(&sMemoryList) ResourceTypeList;
new(&sPortList) ResourceTypeList;
new(&sDMAChannelList) ResourceTypeList;
}