#include <mach/boolean.h>
#include <mach/kern_return.h>
#include <ipc/ipc_port.h>
#include <kern/kalloc.h>
#include <kern/lock.h>
#include <kern/queue.h>
#include <kern/thread.h>
#include <kern/misc_protos.h>
#include <i386/io_port.h>
#include <i386/iopb.h>
#include <i386/seg.h>
#include <i386/iopb_entries.h>
void
iopb_init(void)
{
}
void
iopb_destroy(iopb_tss_t io_tss)
{
}
#if 0
struct io_port {
device_t device;
queue_chain_t dev_list;
queue_chain_t io_use_list;
io_reg_t *io_port_list;
};
typedef struct io_port *io_port_t;
queue_head_t device_to_io_port_list;
struct io_use {
queue_chain_t psq;
queue_chain_t tsq;
io_port_t ps;
iopb_tss_t ts;
};
typedef struct io_use *io_use_t;
decl_simple_lock_data(,iopb_lock)
extern void io_bitmap_init(
isa_iopb bp);
extern void io_bitmap_set(
isa_iopb bp,
io_reg_t *bit_list);
extern void io_bitmap_clear(
isa_iopb bp,
io_reg_t *bit_list);
extern io_port_t device_to_io_port_lookup(
device_t device);
extern void io_tss_init(
iopb_tss_t io_tss);
void
iopb_init(void)
{
queue_init(&device_to_io_port_list);
simple_lock_init(&iopb_lock, ETAP_IO_IOPB);
}
void
io_bitmap_init(
isa_iopb bp)
{
register unsigned char *b;
register int s;
s = sizeof(isa_iopb);
b = bp;
do {
*b++ = ~0;
} while (--s >= 0);
}
void
io_bitmap_set(
isa_iopb bp,
io_reg_t *bit_list)
{
io_reg_t io_bit;
while ((io_bit = *bit_list++) != IO_REG_NULL) {
bp[io_bit>>3] &= ~(1 << (io_bit & 0x7));
}
}
void
io_bitmap_clear(
isa_iopb bp,
io_reg_t *bit_list)
{
io_reg_t io_bit;
while ((io_bit = *bit_list++) != IO_REG_NULL) {
bp[io_bit>>3] |= (1 << (io_bit & 0x7));
}
}
io_port_t
device_to_io_port_lookup(
device_t device)
{
register io_port_t io_port;
queue_iterate(&device_to_io_port_list, io_port, io_port_t, dev_list) {
if (io_port->device == device) {
return io_port;
}
}
return 0;
}
void
io_port_create(
device_t device,
io_reg_t *io_port_list)
{
register io_port_t io_port, old_io_port;
io_port = (io_port_t) kalloc(sizeof(struct io_port));
simple_lock(&iopb_lock);
if (device_to_io_port_lookup(device) != 0) {
simple_unlock(&iopb_lock);
kfree((vm_offset_t) io_port, sizeof(struct io_port));
return;
}
io_port->device = device;
queue_init(&io_port->io_use_list);
io_port->io_port_list = io_port_list;
queue_enter(&device_to_io_port_list, io_port, io_port_t, dev_list);
simple_unlock(&iopb_lock);
}
void
io_port_destroy(
device_t device)
{
io_port_t io_port;
io_use_t iu;
simple_lock(&iopb_lock);
io_port = device_to_io_port_lookup(device);
if (io_port == 0) {
simple_unlock(&iopb_lock);
return;
}
queue_iterate(&io_port->io_use_list, iu, io_use_t, psq) {
iopb_tss_t io_tss;
io_tss = iu->ts;
io_bitmap_clear(io_tss->bitmap, io_port->io_port_list);
queue_remove(&io_tss->io_port_list, iu, io_use_t, tsq);
}
queue_remove(&device_to_io_port_list, io_port, io_port_t, dev_list);
simple_unlock(&iopb_lock);
while (!queue_empty(&io_port->io_use_list)) {
iu = (io_use_t) queue_first(&io_port->io_use_list);
queue_remove(&io_port->io_use_list, iu, io_use_t, psq);
kfree((vm_offset_t)iu, sizeof(struct io_use));
}
kfree((vm_offset_t)io_port, sizeof(struct io_port));
}
void
io_tss_init(
iopb_tss_t io_tss)
{
vm_offset_t addr = (vm_offset_t) io_tss;
vm_size_t size = (char *)&io_tss->barrier - (char *)io_tss;
bzero((char *)&io_tss->tss, sizeof(struct i386_tss));
io_tss->tss.io_bit_map_offset
= (char *)&io_tss->bitmap - (char *)io_tss;
io_tss->tss.ss0 = KERNEL_DS;
io_bitmap_init(io_tss->bitmap);
io_tss->barrier = ~0;
queue_init(&io_tss->io_port_list);
addr += LINEAR_KERNEL_ADDRESS;
io_tss->iopb_desc[0] = ((size-1) & 0xffff)
| ((addr & 0xffff) << 16);
io_tss->iopb_desc[1] = ((addr & 0x00ff0000) >> 16)
| ((ACC_TSS|ACC_PL_K|ACC_P) << 8)
| ((size-1) & 0x000f0000)
| (addr & 0xff000000);
}
iopb_tss_t
iopb_create(void)
{
register iopb_tss_t ts;
ts = (iopb_tss_t) kalloc(sizeof (struct iopb_tss));
io_tss_init(ts);
return (ts);
}
void
iopb_destroy(
iopb_tss_t io_tss)
{
io_use_t iu;
io_port_t io_port;
simple_lock(&iopb_lock);
queue_iterate(&io_tss->io_port_list, iu, io_use_t, tsq) {
io_port = iu->ps;
queue_remove(&io_port->io_use_list, iu, io_use_t, psq);
}
simple_unlock(&iopb_lock);
while (!queue_empty(&io_tss->io_port_list)) {
iu = (io_use_t) queue_first(&io_tss->io_port_list);
queue_remove(&io_tss->io_port_list, iu, io_use_t, tsq);
kfree((vm_offset_t)iu, sizeof(struct io_use));
}
kfree((vm_offset_t)io_tss, sizeof(struct iopb_tss));
}
kern_return_t
i386_io_port_add(
thread_t thread,
device_t device)
{
pcb_t pcb;
iopb_tss_t io_tss, new_io_tss;
io_port_t io_port;
io_use_t iu, old_iu;
if (thread == THREAD_NULL
|| device == DEVICE_NULL)
return KERN_INVALID_ARGUMENT;
pcb = thread->top_act->mact.pcb;
new_io_tss = 0;
iu = (io_use_t) kalloc(sizeof(struct io_use));
Retry:
simple_lock(&iopb_lock);
io_port = device_to_io_port_lookup(device);
if (io_port == 0) {
simple_unlock(&iopb_lock);
if (new_io_tss)
kfree((vm_offset_t)new_io_tss, sizeof(struct iopb_tss));
kfree((vm_offset_t) iu, sizeof(struct io_use));
return KERN_INVALID_ARGUMENT;
}
simple_lock(&pcb->lock);
io_tss = pcb->ims.io_tss;
if (io_tss == 0) {
if (new_io_tss == 0) {
simple_unlock(&pcb->lock);
simple_unlock(&iopb_lock);
new_io_tss = (iopb_tss_t) kalloc(sizeof(struct iopb_tss));
io_tss_init(new_io_tss);
goto Retry;
}
io_tss = new_io_tss;
pcb->ims.io_tss = io_tss;
new_io_tss = 0;
}
queue_iterate(&io_tss->io_port_list, old_iu, io_use_t, tsq) {
if (old_iu->ps == io_port) {
simple_unlock(&pcb->lock);
simple_unlock(&iopb_lock);
kfree((vm_offset_t)iu, sizeof(struct io_use));
if (new_io_tss)
kfree((vm_offset_t)new_io_tss, sizeof(struct iopb_tss));
return KERN_SUCCESS;
}
}
iu->ps = io_port;
iu->ts = io_tss;
queue_enter(&io_port->io_use_list, iu, io_use_t, psq);
queue_enter(&io_tss->io_port_list, iu, io_use_t, tsq);
io_bitmap_set(io_tss->bitmap, io_port->io_port_list);
simple_unlock(&pcb->lock);
simple_unlock(&iopb_lock);
if (new_io_tss)
kfree((vm_offset_t)new_io_tss, sizeof(struct iopb_tss));
return KERN_SUCCESS;
}
kern_return_t
i386_io_port_remove(
thread_t thread,
device_t device)
{
pcb_t pcb;
iopb_tss_t io_tss;
io_port_t io_port;
io_use_t iu;
if (thread == THREAD_NULL
|| device == DEVICE_NULL)
return KERN_INVALID_ARGUMENT;
pcb = thread->top_act->mact.pcb;
simple_lock(&iopb_lock);
io_port = device_to_io_port_lookup(device);
if (io_port == 0) {
simple_unlock(&iopb_lock);
return KERN_INVALID_ARGUMENT;
}
simple_lock(&pcb->lock);
io_tss = pcb->ims.io_tss;
if (io_tss == 0) {
simple_unlock(&pcb->lock);
simple_unlock(&iopb_lock);
return KERN_INVALID_ARGUMENT;
}
queue_iterate(&io_tss->io_port_list, iu, io_use_t, tsq) {
if (iu->ps == io_port) {
io_bitmap_clear(io_tss->bitmap, io_port->io_port_list);
queue_remove(&io_port->io_use_list, iu, io_use_t, psq);
queue_remove(&io_tss->io_port_list, iu, io_use_t, tsq);
simple_unlock(&pcb->lock);
simple_unlock(&iopb_lock);
kfree((vm_offset_t)iu, sizeof(struct io_use));
return KERN_SUCCESS;
}
}
return KERN_INVALID_ARGUMENT;
}
kern_return_t
i386_io_port_list(thread, list, list_count)
thread_t thread;
device_t **list;
unsigned int *list_count;
{
register pcb_t pcb;
register iopb_tss_t io_tss;
unsigned int count, alloc_count;
device_t *devices;
vm_size_t size_needed, size;
vm_offset_t addr;
int i;
if (thread == THREAD_NULL)
return KERN_INVALID_ARGUMENT;
pcb = thread->top_act->mact.pcb;
alloc_count = 16;
do {
size_needed = alloc_count * sizeof(ipc_port_t);
if (size_needed <= size)
break;
if (size != 0)
kfree(addr, size);
assert(size_needed > 0);
size = size_needed;
addr = kalloc(size);
if (addr == 0)
return KERN_RESOURCE_SHORTAGE;
devices = (device_t *)addr;
count = 0;
simple_lock(&iopb_lock);
simple_lock(&pcb->lock);
io_tss = pcb->ims.io_tss;
if (io_tss != 0) {
register io_use_t iu;
queue_iterate(&io_tss->io_port_list, iu, io_use_t, tsq) {
if (++count < alloc_count) {
*devices = iu->ps->device;
device_reference(*devices);
devices++;
}
}
}
simple_unlock(&pcb->lock);
simple_unlock(&iopb_lock);
} while (count > alloc_count);
if (count == 0) {
*list = 0;
*list_count = 0;
if (size != 0)
kfree(addr, size);
}
else {
size_needed = count * sizeof(ipc_port_t);
if (size_needed < size) {
vm_offset_t new_addr;
new_addr = kalloc(size_needed);
if (new_addr == 0) {
for (i = 0; i < count; i++)
device_deallocate(devices[i]);
kfree(addr, size);
return KERN_RESOURCE_SHORTAGE;
}
bcopy((char *)addr, (char *)new_addr, size_needed);
kfree(addr, size);
devices = (device_t *)new_addr;
}
for (i = 0; i < count; i++)
((ipc_port_t *)devices)[i] =
convert_device_to_port(devices[i]);
}
*list = devices;
*list_count = count;
return KERN_SUCCESS;
}
boolean_t
iopb_check_mapping(
thread_t thread,
device_t device)
{
pcb_t pcb;
io_port_t io_port;
io_use_t iu;
pcb = thread->top_act->mact.pcb;
simple_lock(&iopb_lock);
io_port = device_to_io_port_lookup(device);
if (io_port == 0) {
simple_unlock(&iopb_lock);
return FALSE;
}
queue_iterate(&io_port->io_use_list, iu, io_use_t, psq) {
if (iu->ts == pcb->ims.io_tss) {
simple_unlock(&iopb_lock);
return TRUE;
}
}
simple_unlock(&iopb_lock);
return FALSE;
}
#endif