mirror of
https://github.com/NVIDIA/open-gpu-kernel-modules.git
synced 2024-12-13 15:08:59 +01:00
413 lines
10 KiB
C
413 lines
10 KiB
C
/*
|
|
* SPDX-FileCopyrightText: Copyright (c) 2012-2013 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
* SPDX-License-Identifier: MIT
|
|
*
|
|
* 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
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS 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 "os-interface.h"
|
|
#include "nv-linux.h"
|
|
#include "nv-reg.h"
|
|
#include "nv-frontend.h"
|
|
|
|
#if defined(MODULE_LICENSE)
|
|
|
|
MODULE_LICENSE("Dual MIT/GPL");
|
|
|
|
|
|
|
|
#endif
|
|
#if defined(MODULE_INFO)
|
|
MODULE_INFO(supported, "external");
|
|
#endif
|
|
#if defined(MODULE_VERSION)
|
|
MODULE_VERSION(NV_VERSION_STRING);
|
|
#endif
|
|
|
|
#ifdef MODULE_ALIAS_CHARDEV_MAJOR
|
|
MODULE_ALIAS_CHARDEV_MAJOR(NV_MAJOR_DEVICE_NUMBER);
|
|
#endif
|
|
|
|
/*
|
|
* MODULE_IMPORT_NS() is added by commit id 8651ec01daeda
|
|
* ("module: add support for symbol namespaces") in 5.4
|
|
*/
|
|
#if defined(MODULE_IMPORT_NS)
|
|
|
|
|
|
/*
|
|
* DMA_BUF namespace is added by commit id 16b0314aa746
|
|
* ("dma-buf: move dma-buf symbols into the DMA_BUF module namespace") in 5.16
|
|
*/
|
|
MODULE_IMPORT_NS(DMA_BUF);
|
|
|
|
|
|
#endif
|
|
|
|
static NvU32 nv_num_instances;
|
|
|
|
// lock required to protect table.
|
|
struct semaphore nv_module_table_lock;
|
|
|
|
// minor number table
|
|
nvidia_module_t *nv_minor_num_table[NV_FRONTEND_CONTROL_DEVICE_MINOR_MAX + 1];
|
|
|
|
int nvidia_init_module(void);
|
|
void nvidia_exit_module(void);
|
|
|
|
/* EXPORTS to Linux Kernel */
|
|
|
|
int nvidia_frontend_open(struct inode *, struct file *);
|
|
int nvidia_frontend_close(struct inode *, struct file *);
|
|
unsigned int nvidia_frontend_poll(struct file *, poll_table *);
|
|
int nvidia_frontend_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
|
|
long nvidia_frontend_unlocked_ioctl(struct file *, unsigned int, unsigned long);
|
|
long nvidia_frontend_compat_ioctl(struct file *, unsigned int, unsigned long);
|
|
int nvidia_frontend_mmap(struct file *, struct vm_area_struct *);
|
|
|
|
/* character driver entry points */
|
|
static struct file_operations nv_frontend_fops = {
|
|
.owner = THIS_MODULE,
|
|
.poll = nvidia_frontend_poll,
|
|
#if defined(NV_FILE_OPERATIONS_HAS_IOCTL)
|
|
.ioctl = nvidia_frontend_ioctl,
|
|
#endif
|
|
.unlocked_ioctl = nvidia_frontend_unlocked_ioctl,
|
|
#if NVCPU_IS_X86_64 || NVCPU_IS_AARCH64
|
|
.compat_ioctl = nvidia_frontend_compat_ioctl,
|
|
#endif
|
|
.mmap = nvidia_frontend_mmap,
|
|
.open = nvidia_frontend_open,
|
|
.release = nvidia_frontend_close,
|
|
};
|
|
|
|
/* Helper functions */
|
|
|
|
static int add_device(nvidia_module_t *module, nv_linux_state_t *device, NvBool all)
|
|
{
|
|
NvU32 i;
|
|
int rc = -1;
|
|
|
|
// look for free a minor number and assign unique minor number to this device
|
|
for (i = 0; i <= NV_FRONTEND_CONTROL_DEVICE_MINOR_MIN; i++)
|
|
{
|
|
if (nv_minor_num_table[i] == NULL)
|
|
{
|
|
nv_minor_num_table[i] = module;
|
|
device->minor_num = i;
|
|
if (all == NV_TRUE)
|
|
{
|
|
device = device->next;
|
|
if (device == NULL)
|
|
{
|
|
rc = 0;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
rc = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
static int remove_device(nvidia_module_t *module, nv_linux_state_t *device)
|
|
{
|
|
int rc = -1;
|
|
|
|
// remove this device from minor_number table
|
|
if ((device != NULL) && (nv_minor_num_table[device->minor_num] != NULL))
|
|
{
|
|
nv_minor_num_table[device->minor_num] = NULL;
|
|
device->minor_num = 0;
|
|
rc = 0;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/* Export functions */
|
|
|
|
int nvidia_register_module(nvidia_module_t *module)
|
|
{
|
|
int rc = 0;
|
|
NvU32 ctrl_minor_num;
|
|
|
|
down(&nv_module_table_lock);
|
|
if (module->instance >= NV_MAX_MODULE_INSTANCES)
|
|
{
|
|
printk("NVRM: NVIDIA module instance %d registration failed.\n",
|
|
module->instance);
|
|
rc = -EINVAL;
|
|
goto done;
|
|
}
|
|
|
|
ctrl_minor_num = NV_FRONTEND_CONTROL_DEVICE_MINOR_MAX - module->instance;
|
|
nv_minor_num_table[ctrl_minor_num] = module;
|
|
nv_num_instances++;
|
|
done:
|
|
up(&nv_module_table_lock);
|
|
|
|
return rc;
|
|
}
|
|
EXPORT_SYMBOL(nvidia_register_module);
|
|
|
|
int nvidia_unregister_module(nvidia_module_t *module)
|
|
{
|
|
int rc = 0;
|
|
NvU32 ctrl_minor_num;
|
|
|
|
down(&nv_module_table_lock);
|
|
|
|
ctrl_minor_num = NV_FRONTEND_CONTROL_DEVICE_MINOR_MAX - module->instance;
|
|
if (nv_minor_num_table[ctrl_minor_num] == NULL)
|
|
{
|
|
printk("NVRM: NVIDIA module for %d instance does not exist\n",
|
|
module->instance);
|
|
rc = -1;
|
|
}
|
|
else
|
|
{
|
|
nv_minor_num_table[ctrl_minor_num] = NULL;
|
|
nv_num_instances--;
|
|
}
|
|
|
|
up(&nv_module_table_lock);
|
|
|
|
return rc;
|
|
}
|
|
EXPORT_SYMBOL(nvidia_unregister_module);
|
|
|
|
int nvidia_frontend_add_device(nvidia_module_t *module, nv_linux_state_t * device)
|
|
{
|
|
int rc = -1;
|
|
NvU32 ctrl_minor_num;
|
|
|
|
down(&nv_module_table_lock);
|
|
ctrl_minor_num = NV_FRONTEND_CONTROL_DEVICE_MINOR_MAX - module->instance;
|
|
if (nv_minor_num_table[ctrl_minor_num] == NULL)
|
|
{
|
|
printk("NVRM: NVIDIA module for %d instance does not exist\n",
|
|
module->instance);
|
|
rc = -1;
|
|
}
|
|
else
|
|
{
|
|
rc = add_device(module, device, NV_FALSE);
|
|
}
|
|
up(&nv_module_table_lock);
|
|
|
|
return rc;
|
|
}
|
|
EXPORT_SYMBOL(nvidia_frontend_add_device);
|
|
|
|
int nvidia_frontend_remove_device(nvidia_module_t *module, nv_linux_state_t * device)
|
|
{
|
|
int rc = 0;
|
|
NvU32 ctrl_minor_num;
|
|
|
|
down(&nv_module_table_lock);
|
|
ctrl_minor_num = NV_FRONTEND_CONTROL_DEVICE_MINOR_MAX - module->instance;
|
|
if (nv_minor_num_table[ctrl_minor_num] == NULL)
|
|
{
|
|
printk("NVRM: NVIDIA module for %d instance does not exist\n",
|
|
module->instance);
|
|
rc = -1;
|
|
}
|
|
else
|
|
{
|
|
rc = remove_device(module, device);
|
|
}
|
|
up(&nv_module_table_lock);
|
|
|
|
return rc;
|
|
}
|
|
EXPORT_SYMBOL(nvidia_frontend_remove_device);
|
|
|
|
int nvidia_frontend_open(
|
|
struct inode *inode,
|
|
struct file *file
|
|
)
|
|
{
|
|
int rc = -ENODEV;
|
|
nvidia_module_t *module = NULL;
|
|
|
|
NvU32 minor_num = NV_FRONTEND_MINOR_NUMBER(inode);
|
|
|
|
down(&nv_module_table_lock);
|
|
module = nv_minor_num_table[minor_num];
|
|
|
|
if ((module != NULL) && (module->open != NULL))
|
|
{
|
|
// Increment the reference count of module to ensure that module does
|
|
// not get unloaded if its corresponding device file is open, for
|
|
// example nvidiaN.ko should not get unloaded if /dev/nvidiaN is open.
|
|
if (!try_module_get(module->owner))
|
|
{
|
|
up(&nv_module_table_lock);
|
|
return -ENODEV;
|
|
}
|
|
rc = module->open(inode, file);
|
|
if (rc < 0)
|
|
{
|
|
module_put(module->owner);
|
|
}
|
|
}
|
|
|
|
up(&nv_module_table_lock);
|
|
return rc;
|
|
}
|
|
|
|
int nvidia_frontend_close(
|
|
struct inode *inode,
|
|
struct file *file
|
|
)
|
|
{
|
|
int rc = -ENODEV;
|
|
nvidia_module_t *module = NULL;
|
|
|
|
NvU32 minor_num = NV_FRONTEND_MINOR_NUMBER(inode);
|
|
|
|
module = nv_minor_num_table[minor_num];
|
|
|
|
if ((module != NULL) && (module->close != NULL))
|
|
{
|
|
rc = module->close(inode, file);
|
|
|
|
// Decrement the reference count of module.
|
|
module_put(module->owner);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
unsigned int nvidia_frontend_poll(
|
|
struct file *file,
|
|
poll_table *wait
|
|
)
|
|
{
|
|
unsigned int mask = 0;
|
|
struct inode *inode = NV_FILE_INODE(file);
|
|
NvU32 minor_num = NV_FRONTEND_MINOR_NUMBER(inode);
|
|
nvidia_module_t *module = nv_minor_num_table[minor_num];
|
|
|
|
if ((module != NULL) && (module->poll != NULL))
|
|
mask = module->poll(file, wait);
|
|
|
|
return mask;
|
|
}
|
|
|
|
int nvidia_frontend_ioctl(
|
|
struct inode *inode,
|
|
struct file *file,
|
|
unsigned int cmd,
|
|
unsigned long i_arg)
|
|
{
|
|
int rc = -ENODEV;
|
|
nvidia_module_t *module = NULL;
|
|
|
|
NvU32 minor_num = NV_FRONTEND_MINOR_NUMBER(inode);
|
|
module = nv_minor_num_table[minor_num];
|
|
|
|
if ((module != NULL) && (module->ioctl != NULL))
|
|
rc = module->ioctl(inode, file, cmd, i_arg);
|
|
|
|
return rc;
|
|
}
|
|
|
|
long nvidia_frontend_unlocked_ioctl(
|
|
struct file *file,
|
|
unsigned int cmd,
|
|
unsigned long i_arg
|
|
)
|
|
{
|
|
return nvidia_frontend_ioctl(NV_FILE_INODE(file), file, cmd, i_arg);
|
|
}
|
|
|
|
long nvidia_frontend_compat_ioctl(
|
|
struct file *file,
|
|
unsigned int cmd,
|
|
unsigned long i_arg
|
|
)
|
|
{
|
|
return nvidia_frontend_ioctl(NV_FILE_INODE(file), file, cmd, i_arg);
|
|
}
|
|
|
|
int nvidia_frontend_mmap(
|
|
struct file *file,
|
|
struct vm_area_struct *vma
|
|
)
|
|
{
|
|
int rc = -ENODEV;
|
|
struct inode *inode = NV_FILE_INODE(file);
|
|
NvU32 minor_num = NV_FRONTEND_MINOR_NUMBER(inode);
|
|
nvidia_module_t *module = nv_minor_num_table[minor_num];
|
|
|
|
if ((module != NULL) && (module->mmap != NULL))
|
|
rc = module->mmap(file, vma);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int __init nvidia_frontend_init_module(void)
|
|
{
|
|
int status = 0;
|
|
|
|
// initialise nvidia module table;
|
|
nv_num_instances = 0;
|
|
memset(nv_minor_num_table, 0, sizeof(nv_minor_num_table));
|
|
NV_INIT_MUTEX(&nv_module_table_lock);
|
|
|
|
status = nvidia_init_module();
|
|
if (status < 0)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
// register char device
|
|
status = register_chrdev(NV_MAJOR_DEVICE_NUMBER, "nvidia-frontend", &nv_frontend_fops);
|
|
if (status < 0)
|
|
{
|
|
printk("NVRM: register_chrdev() failed!\n");
|
|
nvidia_exit_module();
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
static void __exit nvidia_frontend_exit_module(void)
|
|
{
|
|
/*
|
|
* If this is the last nvidia_module to be unregistered, cleanup and
|
|
* unregister char dev
|
|
*/
|
|
if (nv_num_instances == 1)
|
|
{
|
|
unregister_chrdev(NV_MAJOR_DEVICE_NUMBER, "nvidia-frontend");
|
|
}
|
|
|
|
nvidia_exit_module();
|
|
}
|
|
|
|
module_init(nvidia_frontend_init_module);
|
|
module_exit(nvidia_frontend_exit_module);
|
|
|