/*
* AGPGART driver frontend
* Copyright (C) 2004 Silicon Graphics, Inc.
* Copyright (C) 2002-2003 Dave Jones
* Copyright (C) 1999 Jeff Hartmann
* Copyright (C) 1999 Precision Insight, Inc.
* Copyright (C) 1999 Xi Graphics, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* JEFF HARTMANN, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mman.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/agp_backend.h>
#include <linux/agpgart.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
#include "agp.h"
struct agp_front_data agp_fe;
struct agp_memory *agp_find_mem_by_key(int key)
{
struct agp_memory *curr;
if (agp_fe.current_controller == NULL)
return NULL;
curr = agp_fe.current_controller->pool;
while (curr != NULL) {
if (curr->key == key)
break;
curr = curr->next;
}
DBG("key=%d -> mem=%p", key, curr);
return curr;
}
static void agp_remove_from_pool(struct agp_memory *temp)
{
struct agp_memory *prev;
struct agp_memory *next;
/* Check to see if this is even in the memory pool */
DBG("mem=%p", temp);
if (agp_find_mem_by_key(temp->key) != NULL) {
next = temp->next;
prev = temp->prev;
if (prev != NULL) {
prev->next = next;
if (next != NULL)
next->prev = prev;
} else {
/* This is the first item on the list */
if (next != NULL)
next->prev = NULL;
agp_fe.current_controller->pool = next;
}
}
}
/*
* Routines for managing each client's segment list -
* These routines handle adding and removing segments
* to each auth'ed client.
*/
static struct
agp_segment_priv *agp_find_seg_in_client(const struct agp_client *client,
unsigned long offset,
int size, pgprot_t page_prot)
{
struct agp_segment_priv *seg;
int num_segments, i;
off_t pg_start;
size_t pg_count;
pg_start = offset / 4096;
pg_count = size / 4096;
seg = *(client->segments);
num_segments = client->num_segments;
for (i = 0; i < client->num_segments; i++) {
if ((seg[i].pg_start == pg_start) &&
(seg[i].pg_count == pg_count) &&
(pgprot_val(seg[i].prot) == pgprot_val(page_prot))) {
return seg + i;
}
}
return NULL;
}
static void agp_remove_seg_from_client(struct agp_client *client)
{
DBG("client=%p", client);
if (client->segments != NULL) {
if (*(client->segments) != NULL) {
DBG("Freeing %p from client %p", *(client->segments), client);
kfree(*(client->segments));
}
DBG("Freeing %p from client %p", client->segments, client);
kfree(client->segments);
client->segments = NULL;
}
}
static void agp_add_seg_to_client(struct agp_client *client,
struct agp_segment_priv ** seg, int num_segments)
{