162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * AGPGART driver frontend 362306a36Sopenharmony_ci * Copyright (C) 2004 Silicon Graphics, Inc. 462306a36Sopenharmony_ci * Copyright (C) 2002-2003 Dave Jones 562306a36Sopenharmony_ci * Copyright (C) 1999 Jeff Hartmann 662306a36Sopenharmony_ci * Copyright (C) 1999 Precision Insight, Inc. 762306a36Sopenharmony_ci * Copyright (C) 1999 Xi Graphics, Inc. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 1062306a36Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 1162306a36Sopenharmony_ci * to deal in the Software without restriction, including without limitation 1262306a36Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 1362306a36Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 1462306a36Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * The above copyright notice and this permission notice shall be included 1762306a36Sopenharmony_ci * in all copies or substantial portions of the Software. 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 2062306a36Sopenharmony_ci * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 2162306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 2262306a36Sopenharmony_ci * JEFF HARTMANN, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM, 2362306a36Sopenharmony_ci * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 2462306a36Sopenharmony_ci * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 2562306a36Sopenharmony_ci * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include <linux/types.h> 3062306a36Sopenharmony_ci#include <linux/kernel.h> 3162306a36Sopenharmony_ci#include <linux/module.h> 3262306a36Sopenharmony_ci#include <linux/mman.h> 3362306a36Sopenharmony_ci#include <linux/pci.h> 3462306a36Sopenharmony_ci#include <linux/miscdevice.h> 3562306a36Sopenharmony_ci#include <linux/agp_backend.h> 3662306a36Sopenharmony_ci#include <linux/agpgart.h> 3762306a36Sopenharmony_ci#include <linux/slab.h> 3862306a36Sopenharmony_ci#include <linux/mm.h> 3962306a36Sopenharmony_ci#include <linux/fs.h> 4062306a36Sopenharmony_ci#include <linux/sched.h> 4162306a36Sopenharmony_ci#include <linux/uaccess.h> 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#include "agp.h" 4462306a36Sopenharmony_ci#include "compat_ioctl.h" 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistruct agp_front_data agp_fe; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistruct agp_memory *agp_find_mem_by_key(int key) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci struct agp_memory *curr; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci if (agp_fe.current_controller == NULL) 5362306a36Sopenharmony_ci return NULL; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci curr = agp_fe.current_controller->pool; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci while (curr != NULL) { 5862306a36Sopenharmony_ci if (curr->key == key) 5962306a36Sopenharmony_ci break; 6062306a36Sopenharmony_ci curr = curr->next; 6162306a36Sopenharmony_ci } 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci DBG("key=%d -> mem=%p", key, curr); 6462306a36Sopenharmony_ci return curr; 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic void agp_remove_from_pool(struct agp_memory *temp) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci struct agp_memory *prev; 7062306a36Sopenharmony_ci struct agp_memory *next; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci /* Check to see if this is even in the memory pool */ 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci DBG("mem=%p", temp); 7562306a36Sopenharmony_ci if (agp_find_mem_by_key(temp->key) != NULL) { 7662306a36Sopenharmony_ci next = temp->next; 7762306a36Sopenharmony_ci prev = temp->prev; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci if (prev != NULL) { 8062306a36Sopenharmony_ci prev->next = next; 8162306a36Sopenharmony_ci if (next != NULL) 8262306a36Sopenharmony_ci next->prev = prev; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci } else { 8562306a36Sopenharmony_ci /* This is the first item on the list */ 8662306a36Sopenharmony_ci if (next != NULL) 8762306a36Sopenharmony_ci next->prev = NULL; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci agp_fe.current_controller->pool = next; 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci/* 9562306a36Sopenharmony_ci * Routines for managing each client's segment list - 9662306a36Sopenharmony_ci * These routines handle adding and removing segments 9762306a36Sopenharmony_ci * to each auth'ed client. 9862306a36Sopenharmony_ci */ 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic struct 10162306a36Sopenharmony_ciagp_segment_priv *agp_find_seg_in_client(const struct agp_client *client, 10262306a36Sopenharmony_ci unsigned long offset, 10362306a36Sopenharmony_ci int size, pgprot_t page_prot) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci struct agp_segment_priv *seg; 10662306a36Sopenharmony_ci int i; 10762306a36Sopenharmony_ci off_t pg_start; 10862306a36Sopenharmony_ci size_t pg_count; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci pg_start = offset / 4096; 11162306a36Sopenharmony_ci pg_count = size / 4096; 11262306a36Sopenharmony_ci seg = *(client->segments); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci for (i = 0; i < client->num_segments; i++) { 11562306a36Sopenharmony_ci if ((seg[i].pg_start == pg_start) && 11662306a36Sopenharmony_ci (seg[i].pg_count == pg_count) && 11762306a36Sopenharmony_ci (pgprot_val(seg[i].prot) == pgprot_val(page_prot))) { 11862306a36Sopenharmony_ci return seg + i; 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci return NULL; 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic void agp_remove_seg_from_client(struct agp_client *client) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci DBG("client=%p", client); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (client->segments != NULL) { 13062306a36Sopenharmony_ci if (*(client->segments) != NULL) { 13162306a36Sopenharmony_ci DBG("Freeing %p from client %p", *(client->segments), client); 13262306a36Sopenharmony_ci kfree(*(client->segments)); 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci DBG("Freeing %p from client %p", client->segments, client); 13562306a36Sopenharmony_ci kfree(client->segments); 13662306a36Sopenharmony_ci client->segments = NULL; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic void agp_add_seg_to_client(struct agp_client *client, 14162306a36Sopenharmony_ci struct agp_segment_priv ** seg, int num_segments) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci struct agp_segment_priv **prev_seg; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci prev_seg = client->segments; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (prev_seg != NULL) 14862306a36Sopenharmony_ci agp_remove_seg_from_client(client); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci DBG("Adding seg %p (%d segments) to client %p", seg, num_segments, client); 15162306a36Sopenharmony_ci client->num_segments = num_segments; 15262306a36Sopenharmony_ci client->segments = seg; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic pgprot_t agp_convert_mmap_flags(int prot) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci unsigned long prot_bits; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci prot_bits = calc_vm_prot_bits(prot, 0) | VM_SHARED; 16062306a36Sopenharmony_ci return vm_get_page_prot(prot_bits); 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ciint agp_create_segment(struct agp_client *client, struct agp_region *region) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci struct agp_segment_priv **ret_seg; 16662306a36Sopenharmony_ci struct agp_segment_priv *seg; 16762306a36Sopenharmony_ci struct agp_segment *user_seg; 16862306a36Sopenharmony_ci size_t i; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci seg = kzalloc((sizeof(struct agp_segment_priv) * region->seg_count), GFP_KERNEL); 17162306a36Sopenharmony_ci if (seg == NULL) { 17262306a36Sopenharmony_ci kfree(region->seg_list); 17362306a36Sopenharmony_ci region->seg_list = NULL; 17462306a36Sopenharmony_ci return -ENOMEM; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci user_seg = region->seg_list; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci for (i = 0; i < region->seg_count; i++) { 17962306a36Sopenharmony_ci seg[i].pg_start = user_seg[i].pg_start; 18062306a36Sopenharmony_ci seg[i].pg_count = user_seg[i].pg_count; 18162306a36Sopenharmony_ci seg[i].prot = agp_convert_mmap_flags(user_seg[i].prot); 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci kfree(region->seg_list); 18462306a36Sopenharmony_ci region->seg_list = NULL; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci ret_seg = kmalloc(sizeof(void *), GFP_KERNEL); 18762306a36Sopenharmony_ci if (ret_seg == NULL) { 18862306a36Sopenharmony_ci kfree(seg); 18962306a36Sopenharmony_ci return -ENOMEM; 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci *ret_seg = seg; 19262306a36Sopenharmony_ci agp_add_seg_to_client(client, ret_seg, region->seg_count); 19362306a36Sopenharmony_ci return 0; 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci/* End - Routines for managing each client's segment list */ 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci/* This function must only be called when current_controller != NULL */ 19962306a36Sopenharmony_cistatic void agp_insert_into_pool(struct agp_memory * temp) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci struct agp_memory *prev; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci prev = agp_fe.current_controller->pool; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci if (prev != NULL) { 20662306a36Sopenharmony_ci prev->prev = temp; 20762306a36Sopenharmony_ci temp->next = prev; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci agp_fe.current_controller->pool = temp; 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci/* File private list routines */ 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistruct agp_file_private *agp_find_private(pid_t pid) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci struct agp_file_private *curr; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci curr = agp_fe.file_priv_list; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci while (curr != NULL) { 22262306a36Sopenharmony_ci if (curr->my_pid == pid) 22362306a36Sopenharmony_ci return curr; 22462306a36Sopenharmony_ci curr = curr->next; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci return NULL; 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic void agp_insert_file_private(struct agp_file_private * priv) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci struct agp_file_private *prev; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci prev = agp_fe.file_priv_list; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci if (prev != NULL) 23762306a36Sopenharmony_ci prev->prev = priv; 23862306a36Sopenharmony_ci priv->next = prev; 23962306a36Sopenharmony_ci agp_fe.file_priv_list = priv; 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_cistatic void agp_remove_file_private(struct agp_file_private * priv) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci struct agp_file_private *next; 24562306a36Sopenharmony_ci struct agp_file_private *prev; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci next = priv->next; 24862306a36Sopenharmony_ci prev = priv->prev; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci if (prev != NULL) { 25162306a36Sopenharmony_ci prev->next = next; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci if (next != NULL) 25462306a36Sopenharmony_ci next->prev = prev; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci } else { 25762306a36Sopenharmony_ci if (next != NULL) 25862306a36Sopenharmony_ci next->prev = NULL; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci agp_fe.file_priv_list = next; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci/* End - File flag list routines */ 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci/* 26762306a36Sopenharmony_ci * Wrappers for agp_free_memory & agp_allocate_memory 26862306a36Sopenharmony_ci * These make sure that internal lists are kept updated. 26962306a36Sopenharmony_ci */ 27062306a36Sopenharmony_civoid agp_free_memory_wrap(struct agp_memory *memory) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci agp_remove_from_pool(memory); 27362306a36Sopenharmony_ci agp_free_memory(memory); 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_cistruct agp_memory *agp_allocate_memory_wrap(size_t pg_count, u32 type) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci struct agp_memory *memory; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci memory = agp_allocate_memory(agp_bridge, pg_count, type); 28162306a36Sopenharmony_ci if (memory == NULL) 28262306a36Sopenharmony_ci return NULL; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci agp_insert_into_pool(memory); 28562306a36Sopenharmony_ci return memory; 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci/* Routines for managing the list of controllers - 28962306a36Sopenharmony_ci * These routines manage the current controller, and the list of 29062306a36Sopenharmony_ci * controllers 29162306a36Sopenharmony_ci */ 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic struct agp_controller *agp_find_controller_by_pid(pid_t id) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci struct agp_controller *controller; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci controller = agp_fe.controllers; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci while (controller != NULL) { 30062306a36Sopenharmony_ci if (controller->pid == id) 30162306a36Sopenharmony_ci return controller; 30262306a36Sopenharmony_ci controller = controller->next; 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci return NULL; 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistatic struct agp_controller *agp_create_controller(pid_t id) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci struct agp_controller *controller; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci controller = kzalloc(sizeof(struct agp_controller), GFP_KERNEL); 31362306a36Sopenharmony_ci if (controller == NULL) 31462306a36Sopenharmony_ci return NULL; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci controller->pid = id; 31762306a36Sopenharmony_ci return controller; 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic int agp_insert_controller(struct agp_controller *controller) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci struct agp_controller *prev_controller; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci prev_controller = agp_fe.controllers; 32562306a36Sopenharmony_ci controller->next = prev_controller; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci if (prev_controller != NULL) 32862306a36Sopenharmony_ci prev_controller->prev = controller; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci agp_fe.controllers = controller; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci return 0; 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic void agp_remove_all_clients(struct agp_controller *controller) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci struct agp_client *client; 33862306a36Sopenharmony_ci struct agp_client *temp; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci client = controller->clients; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci while (client) { 34362306a36Sopenharmony_ci struct agp_file_private *priv; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci temp = client; 34662306a36Sopenharmony_ci agp_remove_seg_from_client(temp); 34762306a36Sopenharmony_ci priv = agp_find_private(temp->pid); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci if (priv != NULL) { 35062306a36Sopenharmony_ci clear_bit(AGP_FF_IS_VALID, &priv->access_flags); 35162306a36Sopenharmony_ci clear_bit(AGP_FF_IS_CLIENT, &priv->access_flags); 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci client = client->next; 35462306a36Sopenharmony_ci kfree(temp); 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_cistatic void agp_remove_all_memory(struct agp_controller *controller) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci struct agp_memory *memory; 36162306a36Sopenharmony_ci struct agp_memory *temp; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci memory = controller->pool; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci while (memory) { 36662306a36Sopenharmony_ci temp = memory; 36762306a36Sopenharmony_ci memory = memory->next; 36862306a36Sopenharmony_ci agp_free_memory_wrap(temp); 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic int agp_remove_controller(struct agp_controller *controller) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci struct agp_controller *prev_controller; 37562306a36Sopenharmony_ci struct agp_controller *next_controller; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci prev_controller = controller->prev; 37862306a36Sopenharmony_ci next_controller = controller->next; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci if (prev_controller != NULL) { 38162306a36Sopenharmony_ci prev_controller->next = next_controller; 38262306a36Sopenharmony_ci if (next_controller != NULL) 38362306a36Sopenharmony_ci next_controller->prev = prev_controller; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci } else { 38662306a36Sopenharmony_ci if (next_controller != NULL) 38762306a36Sopenharmony_ci next_controller->prev = NULL; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci agp_fe.controllers = next_controller; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci agp_remove_all_memory(controller); 39362306a36Sopenharmony_ci agp_remove_all_clients(controller); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci if (agp_fe.current_controller == controller) { 39662306a36Sopenharmony_ci agp_fe.current_controller = NULL; 39762306a36Sopenharmony_ci agp_fe.backend_acquired = false; 39862306a36Sopenharmony_ci agp_backend_release(agp_bridge); 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci kfree(controller); 40162306a36Sopenharmony_ci return 0; 40262306a36Sopenharmony_ci} 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_cistatic void agp_controller_make_current(struct agp_controller *controller) 40562306a36Sopenharmony_ci{ 40662306a36Sopenharmony_ci struct agp_client *clients; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci clients = controller->clients; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci while (clients != NULL) { 41162306a36Sopenharmony_ci struct agp_file_private *priv; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci priv = agp_find_private(clients->pid); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci if (priv != NULL) { 41662306a36Sopenharmony_ci set_bit(AGP_FF_IS_VALID, &priv->access_flags); 41762306a36Sopenharmony_ci set_bit(AGP_FF_IS_CLIENT, &priv->access_flags); 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci clients = clients->next; 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci agp_fe.current_controller = controller; 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_cistatic void agp_controller_release_current(struct agp_controller *controller, 42662306a36Sopenharmony_ci struct agp_file_private *controller_priv) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci struct agp_client *clients; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci clear_bit(AGP_FF_IS_VALID, &controller_priv->access_flags); 43162306a36Sopenharmony_ci clients = controller->clients; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci while (clients != NULL) { 43462306a36Sopenharmony_ci struct agp_file_private *priv; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci priv = agp_find_private(clients->pid); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci if (priv != NULL) 43962306a36Sopenharmony_ci clear_bit(AGP_FF_IS_VALID, &priv->access_flags); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci clients = clients->next; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci agp_fe.current_controller = NULL; 44562306a36Sopenharmony_ci agp_fe.used_by_controller = false; 44662306a36Sopenharmony_ci agp_backend_release(agp_bridge); 44762306a36Sopenharmony_ci} 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci/* 45062306a36Sopenharmony_ci * Routines for managing client lists - 45162306a36Sopenharmony_ci * These routines are for managing the list of auth'ed clients. 45262306a36Sopenharmony_ci */ 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_cistatic struct agp_client 45562306a36Sopenharmony_ci*agp_find_client_in_controller(struct agp_controller *controller, pid_t id) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci struct agp_client *client; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci if (controller == NULL) 46062306a36Sopenharmony_ci return NULL; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci client = controller->clients; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci while (client != NULL) { 46562306a36Sopenharmony_ci if (client->pid == id) 46662306a36Sopenharmony_ci return client; 46762306a36Sopenharmony_ci client = client->next; 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci return NULL; 47162306a36Sopenharmony_ci} 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_cistatic struct agp_controller *agp_find_controller_for_client(pid_t id) 47462306a36Sopenharmony_ci{ 47562306a36Sopenharmony_ci struct agp_controller *controller; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci controller = agp_fe.controllers; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci while (controller != NULL) { 48062306a36Sopenharmony_ci if ((agp_find_client_in_controller(controller, id)) != NULL) 48162306a36Sopenharmony_ci return controller; 48262306a36Sopenharmony_ci controller = controller->next; 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci return NULL; 48662306a36Sopenharmony_ci} 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_cistruct agp_client *agp_find_client_by_pid(pid_t id) 48962306a36Sopenharmony_ci{ 49062306a36Sopenharmony_ci struct agp_client *temp; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci if (agp_fe.current_controller == NULL) 49362306a36Sopenharmony_ci return NULL; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci temp = agp_find_client_in_controller(agp_fe.current_controller, id); 49662306a36Sopenharmony_ci return temp; 49762306a36Sopenharmony_ci} 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_cistatic void agp_insert_client(struct agp_client *client) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci struct agp_client *prev_client; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci prev_client = agp_fe.current_controller->clients; 50462306a36Sopenharmony_ci client->next = prev_client; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci if (prev_client != NULL) 50762306a36Sopenharmony_ci prev_client->prev = client; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci agp_fe.current_controller->clients = client; 51062306a36Sopenharmony_ci agp_fe.current_controller->num_clients++; 51162306a36Sopenharmony_ci} 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_cistruct agp_client *agp_create_client(pid_t id) 51462306a36Sopenharmony_ci{ 51562306a36Sopenharmony_ci struct agp_client *new_client; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci new_client = kzalloc(sizeof(struct agp_client), GFP_KERNEL); 51862306a36Sopenharmony_ci if (new_client == NULL) 51962306a36Sopenharmony_ci return NULL; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci new_client->pid = id; 52262306a36Sopenharmony_ci agp_insert_client(new_client); 52362306a36Sopenharmony_ci return new_client; 52462306a36Sopenharmony_ci} 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ciint agp_remove_client(pid_t id) 52762306a36Sopenharmony_ci{ 52862306a36Sopenharmony_ci struct agp_client *client; 52962306a36Sopenharmony_ci struct agp_client *prev_client; 53062306a36Sopenharmony_ci struct agp_client *next_client; 53162306a36Sopenharmony_ci struct agp_controller *controller; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci controller = agp_find_controller_for_client(id); 53462306a36Sopenharmony_ci if (controller == NULL) 53562306a36Sopenharmony_ci return -EINVAL; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci client = agp_find_client_in_controller(controller, id); 53862306a36Sopenharmony_ci if (client == NULL) 53962306a36Sopenharmony_ci return -EINVAL; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci prev_client = client->prev; 54262306a36Sopenharmony_ci next_client = client->next; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci if (prev_client != NULL) { 54562306a36Sopenharmony_ci prev_client->next = next_client; 54662306a36Sopenharmony_ci if (next_client != NULL) 54762306a36Sopenharmony_ci next_client->prev = prev_client; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci } else { 55062306a36Sopenharmony_ci if (next_client != NULL) 55162306a36Sopenharmony_ci next_client->prev = NULL; 55262306a36Sopenharmony_ci controller->clients = next_client; 55362306a36Sopenharmony_ci } 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci controller->num_clients--; 55662306a36Sopenharmony_ci agp_remove_seg_from_client(client); 55762306a36Sopenharmony_ci kfree(client); 55862306a36Sopenharmony_ci return 0; 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci/* End - Routines for managing client lists */ 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci/* File Operations */ 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_cistatic int agp_mmap(struct file *file, struct vm_area_struct *vma) 56662306a36Sopenharmony_ci{ 56762306a36Sopenharmony_ci unsigned int size, current_size; 56862306a36Sopenharmony_ci unsigned long offset; 56962306a36Sopenharmony_ci struct agp_client *client; 57062306a36Sopenharmony_ci struct agp_file_private *priv = file->private_data; 57162306a36Sopenharmony_ci struct agp_kern_info kerninfo; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci mutex_lock(&(agp_fe.agp_mutex)); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci if (agp_fe.backend_acquired != true) 57662306a36Sopenharmony_ci goto out_eperm; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci if (!(test_bit(AGP_FF_IS_VALID, &priv->access_flags))) 57962306a36Sopenharmony_ci goto out_eperm; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci agp_copy_info(agp_bridge, &kerninfo); 58262306a36Sopenharmony_ci size = vma->vm_end - vma->vm_start; 58362306a36Sopenharmony_ci current_size = kerninfo.aper_size; 58462306a36Sopenharmony_ci current_size = current_size * 0x100000; 58562306a36Sopenharmony_ci offset = vma->vm_pgoff << PAGE_SHIFT; 58662306a36Sopenharmony_ci DBG("%lx:%lx", offset, offset+size); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci if (test_bit(AGP_FF_IS_CLIENT, &priv->access_flags)) { 58962306a36Sopenharmony_ci if ((size + offset) > current_size) 59062306a36Sopenharmony_ci goto out_inval; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci client = agp_find_client_by_pid(current->pid); 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci if (client == NULL) 59562306a36Sopenharmony_ci goto out_eperm; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci if (!agp_find_seg_in_client(client, offset, size, vma->vm_page_prot)) 59862306a36Sopenharmony_ci goto out_inval; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci DBG("client vm_ops=%p", kerninfo.vm_ops); 60162306a36Sopenharmony_ci if (kerninfo.vm_ops) { 60262306a36Sopenharmony_ci vma->vm_ops = kerninfo.vm_ops; 60362306a36Sopenharmony_ci } else if (io_remap_pfn_range(vma, vma->vm_start, 60462306a36Sopenharmony_ci (kerninfo.aper_base + offset) >> PAGE_SHIFT, 60562306a36Sopenharmony_ci size, 60662306a36Sopenharmony_ci pgprot_writecombine(vma->vm_page_prot))) { 60762306a36Sopenharmony_ci goto out_again; 60862306a36Sopenharmony_ci } 60962306a36Sopenharmony_ci mutex_unlock(&(agp_fe.agp_mutex)); 61062306a36Sopenharmony_ci return 0; 61162306a36Sopenharmony_ci } 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci if (test_bit(AGP_FF_IS_CONTROLLER, &priv->access_flags)) { 61462306a36Sopenharmony_ci if (size != current_size) 61562306a36Sopenharmony_ci goto out_inval; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci DBG("controller vm_ops=%p", kerninfo.vm_ops); 61862306a36Sopenharmony_ci if (kerninfo.vm_ops) { 61962306a36Sopenharmony_ci vma->vm_ops = kerninfo.vm_ops; 62062306a36Sopenharmony_ci } else if (io_remap_pfn_range(vma, vma->vm_start, 62162306a36Sopenharmony_ci kerninfo.aper_base >> PAGE_SHIFT, 62262306a36Sopenharmony_ci size, 62362306a36Sopenharmony_ci pgprot_writecombine(vma->vm_page_prot))) { 62462306a36Sopenharmony_ci goto out_again; 62562306a36Sopenharmony_ci } 62662306a36Sopenharmony_ci mutex_unlock(&(agp_fe.agp_mutex)); 62762306a36Sopenharmony_ci return 0; 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ciout_eperm: 63162306a36Sopenharmony_ci mutex_unlock(&(agp_fe.agp_mutex)); 63262306a36Sopenharmony_ci return -EPERM; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ciout_inval: 63562306a36Sopenharmony_ci mutex_unlock(&(agp_fe.agp_mutex)); 63662306a36Sopenharmony_ci return -EINVAL; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ciout_again: 63962306a36Sopenharmony_ci mutex_unlock(&(agp_fe.agp_mutex)); 64062306a36Sopenharmony_ci return -EAGAIN; 64162306a36Sopenharmony_ci} 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_cistatic int agp_release(struct inode *inode, struct file *file) 64462306a36Sopenharmony_ci{ 64562306a36Sopenharmony_ci struct agp_file_private *priv = file->private_data; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci mutex_lock(&(agp_fe.agp_mutex)); 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci DBG("priv=%p", priv); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci if (test_bit(AGP_FF_IS_CONTROLLER, &priv->access_flags)) { 65262306a36Sopenharmony_ci struct agp_controller *controller; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci controller = agp_find_controller_by_pid(priv->my_pid); 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci if (controller != NULL) { 65762306a36Sopenharmony_ci if (controller == agp_fe.current_controller) 65862306a36Sopenharmony_ci agp_controller_release_current(controller, priv); 65962306a36Sopenharmony_ci agp_remove_controller(controller); 66062306a36Sopenharmony_ci controller = NULL; 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci } 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci if (test_bit(AGP_FF_IS_CLIENT, &priv->access_flags)) 66562306a36Sopenharmony_ci agp_remove_client(priv->my_pid); 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci agp_remove_file_private(priv); 66862306a36Sopenharmony_ci kfree(priv); 66962306a36Sopenharmony_ci file->private_data = NULL; 67062306a36Sopenharmony_ci mutex_unlock(&(agp_fe.agp_mutex)); 67162306a36Sopenharmony_ci return 0; 67262306a36Sopenharmony_ci} 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_cistatic int agp_open(struct inode *inode, struct file *file) 67562306a36Sopenharmony_ci{ 67662306a36Sopenharmony_ci int minor = iminor(inode); 67762306a36Sopenharmony_ci struct agp_file_private *priv; 67862306a36Sopenharmony_ci struct agp_client *client; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci if (minor != AGPGART_MINOR) 68162306a36Sopenharmony_ci return -ENXIO; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci mutex_lock(&(agp_fe.agp_mutex)); 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci priv = kzalloc(sizeof(struct agp_file_private), GFP_KERNEL); 68662306a36Sopenharmony_ci if (priv == NULL) { 68762306a36Sopenharmony_ci mutex_unlock(&(agp_fe.agp_mutex)); 68862306a36Sopenharmony_ci return -ENOMEM; 68962306a36Sopenharmony_ci } 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci set_bit(AGP_FF_ALLOW_CLIENT, &priv->access_flags); 69262306a36Sopenharmony_ci priv->my_pid = current->pid; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci if (capable(CAP_SYS_RAWIO)) 69562306a36Sopenharmony_ci /* Root priv, can be controller */ 69662306a36Sopenharmony_ci set_bit(AGP_FF_ALLOW_CONTROLLER, &priv->access_flags); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci client = agp_find_client_by_pid(current->pid); 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci if (client != NULL) { 70162306a36Sopenharmony_ci set_bit(AGP_FF_IS_CLIENT, &priv->access_flags); 70262306a36Sopenharmony_ci set_bit(AGP_FF_IS_VALID, &priv->access_flags); 70362306a36Sopenharmony_ci } 70462306a36Sopenharmony_ci file->private_data = (void *) priv; 70562306a36Sopenharmony_ci agp_insert_file_private(priv); 70662306a36Sopenharmony_ci DBG("private=%p, client=%p", priv, client); 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci mutex_unlock(&(agp_fe.agp_mutex)); 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci return 0; 71162306a36Sopenharmony_ci} 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_cistatic int agpioc_info_wrap(struct agp_file_private *priv, void __user *arg) 71462306a36Sopenharmony_ci{ 71562306a36Sopenharmony_ci struct agp_info userinfo; 71662306a36Sopenharmony_ci struct agp_kern_info kerninfo; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci agp_copy_info(agp_bridge, &kerninfo); 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci memset(&userinfo, 0, sizeof(userinfo)); 72162306a36Sopenharmony_ci userinfo.version.major = kerninfo.version.major; 72262306a36Sopenharmony_ci userinfo.version.minor = kerninfo.version.minor; 72362306a36Sopenharmony_ci userinfo.bridge_id = kerninfo.device->vendor | 72462306a36Sopenharmony_ci (kerninfo.device->device << 16); 72562306a36Sopenharmony_ci userinfo.agp_mode = kerninfo.mode; 72662306a36Sopenharmony_ci userinfo.aper_base = kerninfo.aper_base; 72762306a36Sopenharmony_ci userinfo.aper_size = kerninfo.aper_size; 72862306a36Sopenharmony_ci userinfo.pg_total = userinfo.pg_system = kerninfo.max_memory; 72962306a36Sopenharmony_ci userinfo.pg_used = kerninfo.current_memory; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci if (copy_to_user(arg, &userinfo, sizeof(struct agp_info))) 73262306a36Sopenharmony_ci return -EFAULT; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci return 0; 73562306a36Sopenharmony_ci} 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ciint agpioc_acquire_wrap(struct agp_file_private *priv) 73862306a36Sopenharmony_ci{ 73962306a36Sopenharmony_ci struct agp_controller *controller; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci DBG(""); 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci if (!(test_bit(AGP_FF_ALLOW_CONTROLLER, &priv->access_flags))) 74462306a36Sopenharmony_ci return -EPERM; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci if (agp_fe.current_controller != NULL) 74762306a36Sopenharmony_ci return -EBUSY; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci if (!agp_bridge) 75062306a36Sopenharmony_ci return -ENODEV; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci if (atomic_read(&agp_bridge->agp_in_use)) 75362306a36Sopenharmony_ci return -EBUSY; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci atomic_inc(&agp_bridge->agp_in_use); 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci agp_fe.backend_acquired = true; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci controller = agp_find_controller_by_pid(priv->my_pid); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci if (controller != NULL) { 76262306a36Sopenharmony_ci agp_controller_make_current(controller); 76362306a36Sopenharmony_ci } else { 76462306a36Sopenharmony_ci controller = agp_create_controller(priv->my_pid); 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci if (controller == NULL) { 76762306a36Sopenharmony_ci agp_fe.backend_acquired = false; 76862306a36Sopenharmony_ci agp_backend_release(agp_bridge); 76962306a36Sopenharmony_ci return -ENOMEM; 77062306a36Sopenharmony_ci } 77162306a36Sopenharmony_ci agp_insert_controller(controller); 77262306a36Sopenharmony_ci agp_controller_make_current(controller); 77362306a36Sopenharmony_ci } 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci set_bit(AGP_FF_IS_CONTROLLER, &priv->access_flags); 77662306a36Sopenharmony_ci set_bit(AGP_FF_IS_VALID, &priv->access_flags); 77762306a36Sopenharmony_ci return 0; 77862306a36Sopenharmony_ci} 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ciint agpioc_release_wrap(struct agp_file_private *priv) 78162306a36Sopenharmony_ci{ 78262306a36Sopenharmony_ci DBG(""); 78362306a36Sopenharmony_ci agp_controller_release_current(agp_fe.current_controller, priv); 78462306a36Sopenharmony_ci return 0; 78562306a36Sopenharmony_ci} 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ciint agpioc_setup_wrap(struct agp_file_private *priv, void __user *arg) 78862306a36Sopenharmony_ci{ 78962306a36Sopenharmony_ci struct agp_setup mode; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci DBG(""); 79262306a36Sopenharmony_ci if (copy_from_user(&mode, arg, sizeof(struct agp_setup))) 79362306a36Sopenharmony_ci return -EFAULT; 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci agp_enable(agp_bridge, mode.agp_mode); 79662306a36Sopenharmony_ci return 0; 79762306a36Sopenharmony_ci} 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_cistatic int agpioc_reserve_wrap(struct agp_file_private *priv, void __user *arg) 80062306a36Sopenharmony_ci{ 80162306a36Sopenharmony_ci struct agp_region reserve; 80262306a36Sopenharmony_ci struct agp_client *client; 80362306a36Sopenharmony_ci struct agp_file_private *client_priv; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci DBG(""); 80662306a36Sopenharmony_ci if (copy_from_user(&reserve, arg, sizeof(struct agp_region))) 80762306a36Sopenharmony_ci return -EFAULT; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci if ((unsigned) reserve.seg_count >= ~0U/sizeof(struct agp_segment)) 81062306a36Sopenharmony_ci return -EFAULT; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci client = agp_find_client_by_pid(reserve.pid); 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci if (reserve.seg_count == 0) { 81562306a36Sopenharmony_ci /* remove a client */ 81662306a36Sopenharmony_ci client_priv = agp_find_private(reserve.pid); 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci if (client_priv != NULL) { 81962306a36Sopenharmony_ci set_bit(AGP_FF_IS_CLIENT, &client_priv->access_flags); 82062306a36Sopenharmony_ci set_bit(AGP_FF_IS_VALID, &client_priv->access_flags); 82162306a36Sopenharmony_ci } 82262306a36Sopenharmony_ci if (client == NULL) { 82362306a36Sopenharmony_ci /* client is already removed */ 82462306a36Sopenharmony_ci return 0; 82562306a36Sopenharmony_ci } 82662306a36Sopenharmony_ci return agp_remove_client(reserve.pid); 82762306a36Sopenharmony_ci } else { 82862306a36Sopenharmony_ci struct agp_segment *segment; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci if (reserve.seg_count >= 16384) 83162306a36Sopenharmony_ci return -EINVAL; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci segment = kmalloc((sizeof(struct agp_segment) * reserve.seg_count), 83462306a36Sopenharmony_ci GFP_KERNEL); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci if (segment == NULL) 83762306a36Sopenharmony_ci return -ENOMEM; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci if (copy_from_user(segment, (void __user *) reserve.seg_list, 84062306a36Sopenharmony_ci sizeof(struct agp_segment) * reserve.seg_count)) { 84162306a36Sopenharmony_ci kfree(segment); 84262306a36Sopenharmony_ci return -EFAULT; 84362306a36Sopenharmony_ci } 84462306a36Sopenharmony_ci reserve.seg_list = segment; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci if (client == NULL) { 84762306a36Sopenharmony_ci /* Create the client and add the segment */ 84862306a36Sopenharmony_ci client = agp_create_client(reserve.pid); 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci if (client == NULL) { 85162306a36Sopenharmony_ci kfree(segment); 85262306a36Sopenharmony_ci return -ENOMEM; 85362306a36Sopenharmony_ci } 85462306a36Sopenharmony_ci client_priv = agp_find_private(reserve.pid); 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci if (client_priv != NULL) { 85762306a36Sopenharmony_ci set_bit(AGP_FF_IS_CLIENT, &client_priv->access_flags); 85862306a36Sopenharmony_ci set_bit(AGP_FF_IS_VALID, &client_priv->access_flags); 85962306a36Sopenharmony_ci } 86062306a36Sopenharmony_ci } 86162306a36Sopenharmony_ci return agp_create_segment(client, &reserve); 86262306a36Sopenharmony_ci } 86362306a36Sopenharmony_ci /* Will never really happen */ 86462306a36Sopenharmony_ci return -EINVAL; 86562306a36Sopenharmony_ci} 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ciint agpioc_protect_wrap(struct agp_file_private *priv) 86862306a36Sopenharmony_ci{ 86962306a36Sopenharmony_ci DBG(""); 87062306a36Sopenharmony_ci /* This function is not currently implemented */ 87162306a36Sopenharmony_ci return -EINVAL; 87262306a36Sopenharmony_ci} 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_cistatic int agpioc_allocate_wrap(struct agp_file_private *priv, void __user *arg) 87562306a36Sopenharmony_ci{ 87662306a36Sopenharmony_ci struct agp_memory *memory; 87762306a36Sopenharmony_ci struct agp_allocate alloc; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci DBG(""); 88062306a36Sopenharmony_ci if (copy_from_user(&alloc, arg, sizeof(struct agp_allocate))) 88162306a36Sopenharmony_ci return -EFAULT; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci if (alloc.type >= AGP_USER_TYPES) 88462306a36Sopenharmony_ci return -EINVAL; 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci memory = agp_allocate_memory_wrap(alloc.pg_count, alloc.type); 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci if (memory == NULL) 88962306a36Sopenharmony_ci return -ENOMEM; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci alloc.key = memory->key; 89262306a36Sopenharmony_ci alloc.physical = memory->physical; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci if (copy_to_user(arg, &alloc, sizeof(struct agp_allocate))) { 89562306a36Sopenharmony_ci agp_free_memory_wrap(memory); 89662306a36Sopenharmony_ci return -EFAULT; 89762306a36Sopenharmony_ci } 89862306a36Sopenharmony_ci return 0; 89962306a36Sopenharmony_ci} 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ciint agpioc_deallocate_wrap(struct agp_file_private *priv, int arg) 90262306a36Sopenharmony_ci{ 90362306a36Sopenharmony_ci struct agp_memory *memory; 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci DBG(""); 90662306a36Sopenharmony_ci memory = agp_find_mem_by_key(arg); 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci if (memory == NULL) 90962306a36Sopenharmony_ci return -EINVAL; 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci agp_free_memory_wrap(memory); 91262306a36Sopenharmony_ci return 0; 91362306a36Sopenharmony_ci} 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_cistatic int agpioc_bind_wrap(struct agp_file_private *priv, void __user *arg) 91662306a36Sopenharmony_ci{ 91762306a36Sopenharmony_ci struct agp_bind bind_info; 91862306a36Sopenharmony_ci struct agp_memory *memory; 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci DBG(""); 92162306a36Sopenharmony_ci if (copy_from_user(&bind_info, arg, sizeof(struct agp_bind))) 92262306a36Sopenharmony_ci return -EFAULT; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci memory = agp_find_mem_by_key(bind_info.key); 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci if (memory == NULL) 92762306a36Sopenharmony_ci return -EINVAL; 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci return agp_bind_memory(memory, bind_info.pg_start); 93062306a36Sopenharmony_ci} 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_cistatic int agpioc_unbind_wrap(struct agp_file_private *priv, void __user *arg) 93362306a36Sopenharmony_ci{ 93462306a36Sopenharmony_ci struct agp_memory *memory; 93562306a36Sopenharmony_ci struct agp_unbind unbind; 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci DBG(""); 93862306a36Sopenharmony_ci if (copy_from_user(&unbind, arg, sizeof(struct agp_unbind))) 93962306a36Sopenharmony_ci return -EFAULT; 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci memory = agp_find_mem_by_key(unbind.key); 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci if (memory == NULL) 94462306a36Sopenharmony_ci return -EINVAL; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci return agp_unbind_memory(memory); 94762306a36Sopenharmony_ci} 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_cistatic long agp_ioctl(struct file *file, 95062306a36Sopenharmony_ci unsigned int cmd, unsigned long arg) 95162306a36Sopenharmony_ci{ 95262306a36Sopenharmony_ci struct agp_file_private *curr_priv = file->private_data; 95362306a36Sopenharmony_ci int ret_val = -ENOTTY; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci DBG("priv=%p, cmd=%x", curr_priv, cmd); 95662306a36Sopenharmony_ci mutex_lock(&(agp_fe.agp_mutex)); 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci if ((agp_fe.current_controller == NULL) && 95962306a36Sopenharmony_ci (cmd != AGPIOC_ACQUIRE)) { 96062306a36Sopenharmony_ci ret_val = -EINVAL; 96162306a36Sopenharmony_ci goto ioctl_out; 96262306a36Sopenharmony_ci } 96362306a36Sopenharmony_ci if ((agp_fe.backend_acquired != true) && 96462306a36Sopenharmony_ci (cmd != AGPIOC_ACQUIRE)) { 96562306a36Sopenharmony_ci ret_val = -EBUSY; 96662306a36Sopenharmony_ci goto ioctl_out; 96762306a36Sopenharmony_ci } 96862306a36Sopenharmony_ci if (cmd != AGPIOC_ACQUIRE) { 96962306a36Sopenharmony_ci if (!(test_bit(AGP_FF_IS_CONTROLLER, &curr_priv->access_flags))) { 97062306a36Sopenharmony_ci ret_val = -EPERM; 97162306a36Sopenharmony_ci goto ioctl_out; 97262306a36Sopenharmony_ci } 97362306a36Sopenharmony_ci /* Use the original pid of the controller, 97462306a36Sopenharmony_ci * in case it's threaded */ 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci if (agp_fe.current_controller->pid != curr_priv->my_pid) { 97762306a36Sopenharmony_ci ret_val = -EBUSY; 97862306a36Sopenharmony_ci goto ioctl_out; 97962306a36Sopenharmony_ci } 98062306a36Sopenharmony_ci } 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci switch (cmd) { 98362306a36Sopenharmony_ci case AGPIOC_INFO: 98462306a36Sopenharmony_ci ret_val = agpioc_info_wrap(curr_priv, (void __user *) arg); 98562306a36Sopenharmony_ci break; 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci case AGPIOC_ACQUIRE: 98862306a36Sopenharmony_ci ret_val = agpioc_acquire_wrap(curr_priv); 98962306a36Sopenharmony_ci break; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci case AGPIOC_RELEASE: 99262306a36Sopenharmony_ci ret_val = agpioc_release_wrap(curr_priv); 99362306a36Sopenharmony_ci break; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci case AGPIOC_SETUP: 99662306a36Sopenharmony_ci ret_val = agpioc_setup_wrap(curr_priv, (void __user *) arg); 99762306a36Sopenharmony_ci break; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci case AGPIOC_RESERVE: 100062306a36Sopenharmony_ci ret_val = agpioc_reserve_wrap(curr_priv, (void __user *) arg); 100162306a36Sopenharmony_ci break; 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci case AGPIOC_PROTECT: 100462306a36Sopenharmony_ci ret_val = agpioc_protect_wrap(curr_priv); 100562306a36Sopenharmony_ci break; 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci case AGPIOC_ALLOCATE: 100862306a36Sopenharmony_ci ret_val = agpioc_allocate_wrap(curr_priv, (void __user *) arg); 100962306a36Sopenharmony_ci break; 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci case AGPIOC_DEALLOCATE: 101262306a36Sopenharmony_ci ret_val = agpioc_deallocate_wrap(curr_priv, (int) arg); 101362306a36Sopenharmony_ci break; 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci case AGPIOC_BIND: 101662306a36Sopenharmony_ci ret_val = agpioc_bind_wrap(curr_priv, (void __user *) arg); 101762306a36Sopenharmony_ci break; 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci case AGPIOC_UNBIND: 102062306a36Sopenharmony_ci ret_val = agpioc_unbind_wrap(curr_priv, (void __user *) arg); 102162306a36Sopenharmony_ci break; 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci case AGPIOC_CHIPSET_FLUSH: 102462306a36Sopenharmony_ci break; 102562306a36Sopenharmony_ci } 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ciioctl_out: 102862306a36Sopenharmony_ci DBG("ioctl returns %d\n", ret_val); 102962306a36Sopenharmony_ci mutex_unlock(&(agp_fe.agp_mutex)); 103062306a36Sopenharmony_ci return ret_val; 103162306a36Sopenharmony_ci} 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_cistatic const struct file_operations agp_fops = 103462306a36Sopenharmony_ci{ 103562306a36Sopenharmony_ci .owner = THIS_MODULE, 103662306a36Sopenharmony_ci .llseek = no_llseek, 103762306a36Sopenharmony_ci .unlocked_ioctl = agp_ioctl, 103862306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 103962306a36Sopenharmony_ci .compat_ioctl = compat_agp_ioctl, 104062306a36Sopenharmony_ci#endif 104162306a36Sopenharmony_ci .mmap = agp_mmap, 104262306a36Sopenharmony_ci .open = agp_open, 104362306a36Sopenharmony_ci .release = agp_release, 104462306a36Sopenharmony_ci}; 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_cistatic struct miscdevice agp_miscdev = 104762306a36Sopenharmony_ci{ 104862306a36Sopenharmony_ci .minor = AGPGART_MINOR, 104962306a36Sopenharmony_ci .name = "agpgart", 105062306a36Sopenharmony_ci .fops = &agp_fops 105162306a36Sopenharmony_ci}; 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ciint agp_frontend_initialize(void) 105462306a36Sopenharmony_ci{ 105562306a36Sopenharmony_ci memset(&agp_fe, 0, sizeof(struct agp_front_data)); 105662306a36Sopenharmony_ci mutex_init(&(agp_fe.agp_mutex)); 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci if (misc_register(&agp_miscdev)) { 105962306a36Sopenharmony_ci printk(KERN_ERR PFX "unable to get minor: %d\n", AGPGART_MINOR); 106062306a36Sopenharmony_ci return -EIO; 106162306a36Sopenharmony_ci } 106262306a36Sopenharmony_ci return 0; 106362306a36Sopenharmony_ci} 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_civoid agp_frontend_cleanup(void) 106662306a36Sopenharmony_ci{ 106762306a36Sopenharmony_ci misc_deregister(&agp_miscdev); 106862306a36Sopenharmony_ci} 1069