mft: Initial copy of mft source

Add initial mft source of version-4.26.1-3 from
https://www.mellanox.com/downloads/MFT/mft-4.26.1-3-arm64-deb.tgz

bug 4192483
bug 4312056

Change-Id: I77c0af297c9833c3dcbcdfc89b316042548b9af8
Signed-off-by: Bharath H S <bhs@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3069977
(cherry picked from commit d65df2a986469950aab3f323bbee3a3aee0c0308)
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3069972
GVS: Gerrit_Virtual_Submit <buildbot_gerritrpt@nvidia.com>
Reviewed-by: Laxman Dewangan <ldewangan@nvidia.com>
This commit is contained in:
Bharath H S
2024-02-02 10:53:24 -08:00
committed by mobile promotions
parent f451b88fd7
commit bf89e2d98e
38 changed files with 5307 additions and 0 deletions

View File

@@ -0,0 +1,32 @@
KPVER ?= $(shell uname -r)
KSRC ?= /lib/modules/$(KPVER)/build
# Oracle Linux OS.
ifneq ($(shell if (echo $(KPVER) | grep -qE 'uek'); then \
echo "YES"; else echo ""; fi),)
override WITH_MAKE_PARAMS += ctf-dir=$(CWD)/.ctf
endif
NNT_DRIVER_LOCATION = ../../nnt_driver
PACKAGE_NAME = nnt-driver
PACKAGE_VERSION = 1.0.0
PACKAGE_RC = 1
%: %.in
sed \
-e 's/@PACKAGE_NAME@/$(PACKAGE_NAME)/g' \
-e 's/@PACKAGE_VERSION@/$(PACKAGE_VERSION)/g' \
-e 's/@PACKAGE_RC@/$(PACKAGE_RC)/g' \
<$< >$@
obj-m += mst_pci.o
EXTRA_CFLAGS= -I$(PWD)/$(NNT_DRIVER_LOCATION)
mst_pci-objs += $(NNT_DRIVER_LOCATION)/nnt_device.o $(NNT_DRIVER_LOCATION)/nnt_dma.o $(NNT_DRIVER_LOCATION)/nnt_pci_conf_access.o \
$(NNT_DRIVER_LOCATION)/nnt_pci_conf_access_no_vsec.o $(NNT_DRIVER_LOCATION)/nnt_memory_access.o \
$(NNT_DRIVER_LOCATION)/nnt_ioctl.o mst_pci_bc.o
all:
make -C $(KSRC) M=$(PWD) CONFIG_CTF= CONFIG_CC_STACKPROTECTOR_STRONG= $(WITH_MAKE_PARAMS) modules
clean:
make -C $(KSRC) M=$(PWD) clean

View File

@@ -0,0 +1,3 @@
mst_pciconf.ko external
mst_pci.ko external
mst_ppc_pci_reset.ko external

View File

@@ -0,0 +1,436 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/semaphore.h>
#include <linux/uaccess.h>
#include <linux/kernel.h>
#include "nnt_ioctl.h"
#include "nnt_defs.h"
#include "nnt_device.h"
#include "nnt_ioctl_defs.h"
#include "nnt_pci_conf_access.h"
#include "mst_pci_bc.h"
MODULE_AUTHOR("Itay Avraham <itayavr@nvidia.com>");
MODULE_DESCRIPTION("NNT Linux driver (NVIDIA® networking tools driver), this is the backward compatibility driver");
MODULE_LICENSE("Dual BSD/GPL");
struct driver_info nnt_driver_info;
static int major_number = -1;
static char* name = "mst_pci";
#define INIT PCI_INIT
#define STOP PCI_STOP
#define PCI_PARAMS_ PCI_PARAMS
#define CONNECTX_WA PCI_CONNECTX_WA
struct mst_device_data
{
char buffer[MST_BC_BUFFER_SIZE];
int buffer_used;
};
static struct mst_device_data mst_devices[MST_BC_MAX_MINOR];
static int mst_pci_bc_open(struct inode* inode, struct file* file)
{
if (file->private_data)
{
return 0;
}
set_private_data_open(file);
return 0;
}
static ssize_t mst_pci_bc_read(struct file* file, char* buf, size_t count, loff_t* f_pos)
{
struct mst_device_data* mst_device = NULL;
struct nnt_device* nnt_device = NULL;
int* buffer_used = NULL;
char* buffer = NULL;
int minor = 0;
int error = 0;
/* Get the nnt device structure */
error = get_nnt_device(file, &nnt_device);
if (error)
{
minor = iminor(file_inode(file));
mst_device = &mst_devices[minor];
buffer = mst_device->buffer;
buffer_used = &mst_device->buffer_used;
}
else
{
buffer = nnt_device->buffer_bc;
buffer_used = &nnt_device->buffer_used_bc;
error = mutex_lock_nnt(file);
CHECK_ERROR(error);
}
if (*f_pos >= *buffer_used)
{
count = 0;
goto MutexUnlock;
}
if (*f_pos + count > *buffer_used)
{
count = *buffer_used - *f_pos;
}
if (copy_to_user(buf, buffer + *f_pos, count))
{
count = -EFAULT;
goto MutexUnlock;
}
*f_pos += count;
MutexUnlock:
if (nnt_device)
{
mutex_unlock_nnt(file);
}
ReturnOnFinished:
return count;
}
static ssize_t mst_pci_bc_write(struct file* file, const char* buf, size_t count, loff_t* f_pos)
{
struct mst_device_data* mst_device = NULL;
struct nnt_device* nnt_device = NULL;
int* buffer_used = NULL;
char* buffer = NULL;
int minor = 0;
int error = 0;
/* Get the nnt device structure */
error = get_nnt_device(file, &nnt_device);
if (error)
{
minor = iminor(file_inode(file));
mst_device = &mst_devices[minor];
buffer = mst_device->buffer;
buffer_used = &mst_device->buffer_used;
}
else
{
buffer = nnt_device->buffer_bc;
buffer_used = &nnt_device->buffer_used_bc;
error = mutex_lock_nnt(file);
CHECK_ERROR(error);
}
if (*f_pos >= MST_BC_BUFFER_SIZE)
{
count = 0;
goto MutexUnlock;
}
if (*f_pos + count > MST_BC_BUFFER_SIZE)
{
count = MST_BC_BUFFER_SIZE - *f_pos;
}
if (copy_from_user(buffer + *f_pos, buf, count))
{
count = -EFAULT;
goto MutexUnlock;
}
*f_pos += count;
if (*buffer_used < *f_pos)
{
*buffer_used = *f_pos;
}
MutexUnlock:
if (nnt_device)
{
mutex_unlock_nnt(file);
}
ReturnOnFinished:
return count;
}
static inline int noncached_address(unsigned long addr)
{
return addr >= __pa(high_memory);
}
static int mst_pci_mmap(struct file* file, struct vm_area_struct* vma)
{
struct nnt_device* nnt_device = NULL;
unsigned long long offset = 0;
unsigned long vsize = 0;
unsigned long off = 0;
int error = 0;
/* Get the nnt device structure */
error = get_nnt_device(file, &nnt_device);
if (error)
{
goto ReturnOnFinished;
}
off = vma->vm_pgoff << PAGE_SHIFT;
vsize = vma->vm_end - vma->vm_start;
if ((nnt_device->device_pci.bar_size <= off) || (nnt_device->device_pci.bar_size < off + vsize))
{
error = -EINVAL;
goto ReturnOnFinished;
}
offset = nnt_device->device_pci.bar_address + off;
/* Accessing memory above the top the kernel knows about or through
a file pointer that was marked O_SYNC will be done non-cached. */
if (noncached_address(offset) || (file->f_flags & O_SYNC))
{
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
}
error = io_remap_pfn_range(vma, vma->vm_start, offset >> PAGE_SHIFT, vsize, vma->vm_page_prot);
ReturnOnFinished:
return error;
}
static long ioctl(struct file* file, unsigned int command, unsigned long argument)
{
void* user_buffer = (void*)argument;
struct nnt_device* nnt_device = NULL;
int error = 0;
/* By convention, any user gets read access
* and is allowed to use the device.
* Commands with no direction are administration
* commands, and you need write permission
* for this */
if (_IOC_DIR(command) == _IOC_NONE)
{
if (!(file->f_mode & FMODE_WRITE))
{
return -EPERM;
}
}
else
{
if (!(file->f_mode & FMODE_READ))
{
return -EPERM;
}
}
if (command != INIT)
{
error = mutex_lock_nnt(file);
if (error)
{
return 0;
}
CHECK_ERROR(error);
/* Get the nnt device structure */
error = get_nnt_device(file, &nnt_device);
if (error)
{
error = 0;
goto ReturnOnFinished;
}
}
switch (command)
{
case INIT:
{
struct mst_pci_init_st mst_init;
struct pci_bus* bus = NULL;
/* Copy the request from user space. */
if (copy_from_user(&mst_init, user_buffer, sizeof(struct mst_pci_init_st)))
{
return -EFAULT;
}
error = set_private_data_bc(file, mst_init.bus, mst_init.devfn, mst_init.domain);
if (error)
{
return 0;
}
error = mutex_lock_nnt(file);
CHECK_ERROR(error);
/* Get the nnt device structure */
error = get_nnt_device(file, &nnt_device);
if (error)
{
goto ReturnOnFinished;
}
bus = pci_find_bus(mst_init.domain, mst_init.bus);
if (!bus)
{
printk(KERN_ERR "unable to find pci bus for domain: %x and bus: %x\n", mst_init.domain, mst_init.bus);
error = -ENXIO;
goto ReturnOnFinished;
}
nnt_device->pci_device = NULL;
nnt_device->pci_device = pci_get_slot(bus, mst_init.devfn);
if (!nnt_device->pci_device)
{
printk(KERN_ERR "missing pci device");
error = -ENXIO;
goto ReturnOnFinished;
}
if (mst_init.bar >= DEVICE_COUNT_RESOURCE)
{
printk(KERN_ERR "bar offset is too large");
error = -ENXIO;
goto ReturnOnFinished;
}
nnt_device->device_pci.bar_address = nnt_device->pci_device->resource[mst_init.bar].start;
nnt_device->device_pci.bar_size = nnt_device->pci_device->resource[mst_init.bar].end + 1 -
nnt_device->pci_device->resource[mst_init.bar].start;
if (nnt_device->device_pci.bar_size == 1)
{
nnt_device->device_pci.bar_size = 0;
}
nnt_device->buffer_used_bc = 0;
break;
}
case PCI_PARAMS_:
{
struct mst_pci_params_st params;
params.bar = nnt_device->device_pci.bar_address;
params.size = nnt_device->device_pci.bar_size;
if (copy_to_user(user_buffer, &params, sizeof(struct mst_pci_params_st)))
{
error = -EFAULT;
goto ReturnOnFinished;
}
break;
}
case CONNECTX_WA:
{
struct nnt_connectx_wa connectx_wa;
error = pci_connectx_wa(&connectx_wa, nnt_device);
/* Copy the data to the user space. */
if (copy_to_user(user_buffer, &connectx_wa, sizeof(struct nnt_connectx_wa)) != 0)
{
error = -EFAULT;
goto ReturnOnFinished;
}
break;
}
case STOP:
error = destroy_nnt_device_bc(nnt_device);
break;
default:
error = -EINVAL;
break;
}
ReturnOnFinished:
mutex_unlock_nnt(file);
return error;
}
static int mst_release(struct inode* inode, struct file* file)
{
struct nnt_device* nnt_device = NULL;
int error = 0;
/* Get the nnt device structure */
error = get_nnt_device(file, &nnt_device);
if (error)
{
error = 0;
goto ReturnOnFinished;
}
if (nnt_device->memory_device.connectx_wa_slot_p1)
{
unsigned int mask = 0;
error = mutex_lock_nnt(file);
CHECK_ERROR(error);
mask = ~(1 << (nnt_device->memory_device.connectx_wa_slot_p1 - 1));
nnt_device->memory_device.connectx_wa_slot_p1 &= mask; // Fix me
nnt_device->memory_device.connectx_wa_slot_p1 = 0;
mutex_unlock_nnt(file);
}
ReturnOnFinished:
return 0;
}
struct file_operations fop = {.unlocked_ioctl = ioctl,
.open = mst_pci_bc_open,
.write = mst_pci_bc_write,
.read = mst_pci_bc_read,
.mmap = mst_pci_mmap,
.release = mst_release,
.owner = THIS_MODULE};
int with_unknown = 0;
module_param(with_unknown, int, S_IRUSR | S_IWUSR);
static int __init mst_pci_init_module(void)
{
dev_t device_number = -1;
int is_alloc_chrdev_region = 0;
int error = 0;
/* Allocate char driver region and assign major number */
major_number = register_chrdev(0, name, &fop);
if (major_number <= 0)
{
printk(KERN_ERR "Unable to register character mst pci driver.\n");
error = -EINVAL;
}
/* Create device files for MFT. */
error = create_nnt_devices(device_number, is_alloc_chrdev_region, &fop, NNT_PCI_DEVICES, NNT_MELLANOX_PCI_VENDOR,
with_unknown) ||
create_nnt_devices(device_number, is_alloc_chrdev_region, &fop, NNT_PCI_DEVICES, NNT_NVIDIA_PCI_VENDOR,
with_unknown);
return error;
}
static void __exit mst_pci_cleanup_module(void)
{
int is_alloc_chrdev_region = 0;
destroy_nnt_devices(is_alloc_chrdev_region);
unregister_chrdev(major_number, name);
}
module_init(mst_pci_init_module);
module_exit(mst_pci_cleanup_module);

View File

@@ -0,0 +1,31 @@
#ifndef MST_PCI_H
#define MST_PCI_H
/* These will be specific for PCI */
#define PCI_MAGIC 0xD1
#define PCI_INIT _IOC(_IOC_NONE,PCI_MAGIC,0,sizeof(struct mst_pci_init_st))
struct mst_pci_init_st {
unsigned int domain;
unsigned int bus;
unsigned int devfn;
int bar;
};
#define PCI_STOP _IOC(_IOC_NONE,PCI_MAGIC,1,0)
#define PCI_PARAMS _IOR(PCI_MAGIC,2, struct mst_pci_params_st)
struct mst_pci_params_st {
unsigned long long __attribute__((packed)) bar;
unsigned long long __attribute__((packed)) size;
};
#define CONNECTX_WA_BASE 0xf0384 // SEM BASE ADDR. SEM 0xf0380 is reserved for external tools usage.
#define CONNECTX_WA_SIZE 3 // Size in entries
#define PCI_CONNECTX_WA _IOR(PCI_MAGIC,3, u_int32_t)
#endif

View File

@@ -0,0 +1,32 @@
KPVER ?= $(shell uname -r)
KSRC ?= /lib/modules/$(KPVER)/build
# Oracle Linux OS.
ifneq ($(shell if (echo $(KPVER) | grep -qE 'uek'); then \
echo "YES"; else echo ""; fi),)
override WITH_MAKE_PARAMS += ctf-dir=$(CWD)/.ctf
endif
NNT_DRIVER_LOCATION = ../../nnt_driver
PACKAGE_NAME = nnt-driver
PACKAGE_VERSION = 1.0.0
PACKAGE_RC = 1
%: %.in
sed \
-e 's/@PACKAGE_NAME@/$(PACKAGE_NAME)/g' \
-e 's/@PACKAGE_VERSION@/$(PACKAGE_VERSION)/g' \
-e 's/@PACKAGE_RC@/$(PACKAGE_RC)/g' \
<$< >$@
obj-m += mst_pciconf.o
EXTRA_CFLAGS= -I$(PWD)/$(NNT_DRIVER_LOCATION)
mst_pciconf-objs += $(NNT_DRIVER_LOCATION)/nnt_device.o $(NNT_DRIVER_LOCATION)/nnt_dma.o $(NNT_DRIVER_LOCATION)/nnt_pci_conf_access.o \
$(NNT_DRIVER_LOCATION)/nnt_pci_conf_access_no_vsec.o $(NNT_DRIVER_LOCATION)/nnt_memory_access.o \
$(NNT_DRIVER_LOCATION)/nnt_ioctl.o mst_pciconf_bc.o
all:
make -C $(KSRC) M=$(PWD) CONFIG_CTF= CONFIG_CC_STACKPROTECTOR_STRONG= $(WITH_MAKE_PARAMS) modules
clean:
make -C $(KSRC) M=$(PWD) clean

View File

@@ -0,0 +1,3 @@
mst_pciconf.ko external
mst_pci.ko external
mst_ppc_pci_reset.ko external

View File

@@ -0,0 +1,635 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/semaphore.h>
#include <linux/uaccess.h>
#include <linux/kernel.h>
#include "nnt_ioctl.h"
#include "nnt_defs.h"
#include "nnt_device.h"
#include "nnt_ioctl_defs.h"
#include "nnt_pci_conf_access.h"
#include "mst_pciconf_bc.h"
MODULE_AUTHOR("Itay Avraham <itayavr@nvidia.com>");
MODULE_DESCRIPTION("NNT Linux driver (NVIDIA® networking tools driver), this is the backward compatibility driver");
MODULE_LICENSE("Dual BSD/GPL");
struct driver_info nnt_driver_info;
static int major_number = -1;
static char* name = "mst_pciconf";
#define INIT PCICONF_INIT
#define STOP PCICONF_STOP
#define READ4 PCICONF_READ4
#define READ4_NEW PCICONF_READ4_NEW
#define WRITE4 PCICONF_WRITE4
#define WRITE4_NEW PCICONF_WRITE4_NEW
#define MODIFY PCICONF_MODIFY
#define READ4_BUFFER PCICONF_READ4_BUFFER
#define READ4_BUFFER_EX PCICONF_READ4_BUFFER_EX
#define WRITE4_BUFFER PCICONF_WRITE4_BUFFER
#define MST_PARAMS PCICONF_MST_PARAMS
#define MST_META_DATA PCICONF_MST_META_DATA
#define GET_DMA_PAGES PCICONF_GET_DMA_PAGES
#define RELEASE_DMA_PAGES PCICONF_RELEASE_DMA_PAGES
#define READ_DWORD_FROM_CONFIG_SPACE PCICONF_READ_DWORD_FROM_CONFIG_SPACE
static int mst_pciconf_bc_open(struct inode* inode, struct file* file)
{
if (file->private_data)
{
return 0;
}
set_private_data_open(file);
return 0;
}
static ssize_t mst_pciconf_bc_read(struct file* file, char* buf, size_t count, loff_t* f_pos)
{
struct nnt_device* nnt_device = NULL;
int error = 0;
/* Get the nnt device structure */
error = get_nnt_device(file, &nnt_device);
if (error)
{
count = -EFAULT;
goto ReturnOnFinished;
}
error = mutex_lock_nnt(file);
if (*f_pos >= nnt_device->buffer_used_bc)
{
count = 0;
goto MutexUnlock;
}
if (*f_pos + count > nnt_device->buffer_used_bc)
{
count = nnt_device->buffer_used_bc - *f_pos;
}
if (copy_to_user(buf, nnt_device->buffer_bc + *f_pos, count))
{
count = -EFAULT;
goto MutexUnlock;
}
*f_pos += count;
MutexUnlock:
mutex_unlock_nnt(file);
ReturnOnFinished:
return count;
}
static ssize_t mst_pciconf_bc_write(struct file* file, const char* buf, size_t count, loff_t* f_pos)
{
struct nnt_device* nnt_device = NULL;
int error = 0;
/* Get the nnt device structure */
error = get_nnt_device(file, &nnt_device);
if (error)
{
count = -EFAULT;
goto ReturnOnFinished;
}
error = mutex_lock_nnt(file);
if (*f_pos >= MST_BC_BUFFER_SIZE)
{
count = 0;
goto MutexUnlock;
}
if (*f_pos + count > MST_BC_BUFFER_SIZE)
{
count = MST_BC_BUFFER_SIZE - *f_pos;
}
if (copy_from_user(nnt_device->buffer_bc + *f_pos, buf, count))
{
count = -EFAULT;
goto MutexUnlock;
}
*f_pos += count;
if (nnt_device->buffer_used_bc < *f_pos)
{
nnt_device->buffer_used_bc = *f_pos;
}
MutexUnlock:
mutex_unlock_nnt(file);
ReturnOnFinished:
return count;
}
static long ioctl(struct file* file, unsigned int command, unsigned long argument)
{
void* user_buffer = (void*)argument;
struct nnt_device* nnt_device = NULL;
int error = 0;
/* By convention, any user gets read access
* and is allowed to use the device.
* Commands with no direction are administration
* commands, and you need write permission
* for this */
if (_IOC_DIR(command) == _IOC_NONE)
{
if (!(file->f_mode & FMODE_WRITE))
{
return -EPERM;
}
}
else
{
if (!(file->f_mode & FMODE_READ))
{
return -EPERM;
}
}
if (command != INIT)
{
error = mutex_lock_nnt(file);
CHECK_ERROR(error);
/* Get the nnt device structure */
error = get_nnt_device(file, &nnt_device);
if (error)
{
goto ReturnOnFinished;
}
}
switch (command)
{
case INIT:
{
struct nnt_pciconf_init nnt_init;
struct mst_pciconf_init_st mst_init;
struct nnt_device* nnt_device = NULL;
if (copy_from_user(&mst_init, user_buffer, sizeof(struct mst_pciconf_init_st)))
{
error = -EFAULT;
goto ReturnOnFinished;
}
error = set_private_data_bc(file, mst_init.bus, mst_init.devfn, mst_init.domain);
if (error)
{
goto ReturnOnFinished;
}
error = mutex_lock_nnt(file);
CHECK_ERROR(error);
/* Get the nnt device structure */
error = get_nnt_device(file, &nnt_device);
if (error)
{
goto ReturnOnFinished;
}
nnt_init.address_register = mst_init.addr_reg;
nnt_init.address_data_register = mst_init.data_reg;
/* Truncate to 0 length on open for writing. */
if (file->f_flags & O_APPEND)
{
file->f_pos = nnt_device->buffer_used_bc;
}
else if ((file->f_flags & O_TRUNC) || (file->f_flags & O_WRONLY))
{
nnt_device->buffer_used_bc = 0;
}
error = nnt_device->access.init(nnt_device);
break;
}
case WRITE4:
{
struct nnt_rw_operation rw_operation;
struct mst_write4_st mst_write;
/* Copy the request from user space. */
if (copy_from_user(&mst_write, user_buffer, sizeof(struct mst_write4_st)))
{
error = -EFAULT;
goto ReturnOnFinished;
}
rw_operation.data[0] = mst_write.data;
rw_operation.offset = mst_write.offset;
rw_operation.size = 4;
error = nnt_device->access.write(nnt_device, &rw_operation);
break;
}
case WRITE4_NEW:
{
struct nnt_rw_operation rw_operation;
struct mst_write4_new_st mst_write;
/* Copy the request from user space. */
if (copy_from_user(&mst_write, user_buffer, sizeof(struct mst_write4_new_st)))
{
error = -EFAULT;
goto ReturnOnFinished;
}
rw_operation.data[0] = mst_write.data;
rw_operation.offset = mst_write.offset;
rw_operation.address_space = mst_write.address_space;
rw_operation.size = 4;
error = nnt_device->access.write(nnt_device, &rw_operation);
break;
}
case WRITE4_BUFFER:
{
struct mst_write4_buffer_st mst_write;
/* Copy the request from user space. */
if (copy_from_user(&mst_write, user_buffer, sizeof(struct mst_write4_buffer_st)))
{
error = -EFAULT;
goto ReturnOnFinished;
}
error = nnt_device->access.write(nnt_device, (struct nnt_rw_operation*)&mst_write);
if (error)
{
goto ReturnOnFinished;
}
/* No error, return the requested data length. */
error = mst_write.size;
break;
}
case READ4:
{
struct nnt_rw_operation rw_operation;
struct mst_read4_st mst_read;
/* Copy the request from user space. */
if (copy_from_user(&mst_read, user_buffer, sizeof(struct mst_read4_st)))
{
error = -EFAULT;
goto ReturnOnFinished;
}
rw_operation.offset = mst_read.offset;
rw_operation.size = 4;
error = nnt_device->access.read(nnt_device, &rw_operation);
if (error)
{
goto ReturnOnFinished;
}
mst_read.data = rw_operation.data[0];
/* Copy the data to the user space. */
if (copy_to_user(user_buffer, &mst_read, sizeof(struct mst_read4_st)) != 0)
{
error = -EFAULT;
goto ReturnOnFinished;
}
break;
}
case READ4_NEW:
{
struct nnt_rw_operation rw_operation;
struct mst_read4_new_st mst_read;
/* Copy the request from user space. */
if (copy_from_user(&mst_read, user_buffer, sizeof(struct mst_read4_new_st)))
{
error = -EFAULT;
goto ReturnOnFinished;
}
rw_operation.offset = mst_read.offset;
rw_operation.address_space = mst_read.address_space;
rw_operation.size = 4;
error = nnt_device->access.read(nnt_device, &rw_operation);
if (error)
{
goto ReturnOnFinished;
}
mst_read.data = rw_operation.data[0];
/* Copy the data to the user space. */
if (copy_to_user(user_buffer, &mst_read, sizeof(struct mst_read4_new_st)) != 0)
{
error = -EFAULT;
goto ReturnOnFinished;
}
break;
}
case READ4_BUFFER_EX:
case READ4_BUFFER:
{
struct mst_read4_buffer_st mst_read;
/* Copy the request from user space. */
if (copy_from_user(&mst_read, user_buffer, sizeof(struct mst_read4_buffer_st)))
{
error = -EFAULT;
goto ReturnOnFinished;
}
error = nnt_device->access.read(nnt_device, (struct nnt_rw_operation*)&mst_read);
if (error)
{
goto ReturnOnFinished;
}
/* Copy the data to the user space. */
if (copy_to_user(user_buffer, &mst_read, sizeof(struct mst_read4_buffer_st)) != 0)
{
error = -EFAULT;
goto ReturnOnFinished;
}
/* No error, return the requested data length. */
error = mst_read.size;
break;
}
case PCICONF_VPD_READ4:
{
int vpd_default_timeout = 2000;
struct mst_vpd_read4_st mst_vpd_read;
struct nnt_vpd nnt_vpd;
if (!nnt_device->vpd_capability_address)
{
printk(KERN_ERR "Device %s not support Vital Product Data\n", nnt_device->device_name);
error = -ENODEV;
goto ReturnOnFinished;
}
/* Copy the request from user space. */
if (copy_from_user(&mst_vpd_read, user_buffer, sizeof(struct mst_vpd_read4_st)) != 0)
{
error = -EFAULT;
goto ReturnOnFinished;
}
nnt_vpd.offset = mst_vpd_read.offset;
nnt_vpd.data = mst_vpd_read.data;
if (!nnt_vpd.timeout)
{
nnt_vpd.timeout = vpd_default_timeout;
}
error = vpd_read(&nnt_vpd, nnt_device);
if (error)
{
goto ReturnOnFinished;
}
mst_vpd_read.offset = nnt_vpd.offset;
mst_vpd_read.data = nnt_vpd.data;
/* Copy the data to the user space. */
if (copy_to_user(user_buffer, &mst_vpd_read, sizeof(struct mst_vpd_read4_st)) != 0)
{
error = -EFAULT;
goto ReturnOnFinished;
}
break;
}
case PCICONF_VPD_WRITE4:
{
int vpd_default_timeout = 2000;
struct mst_vpd_write4_st mst_vpd_write;
struct nnt_vpd nnt_vpd;
if (!nnt_device->vpd_capability_address)
{
printk(KERN_ERR "Device %s not support Vital Product Data\n", nnt_device->device_name);
error = -ENODEV;
goto ReturnOnFinished;
}
/* Copy the request from user space. */
if (copy_from_user(&mst_vpd_write, user_buffer, sizeof(struct mst_vpd_write4_st)) != 0)
{
error = -EFAULT;
goto ReturnOnFinished;
}
nnt_vpd.offset = mst_vpd_write.offset;
nnt_vpd.data = mst_vpd_write.data;
if (!nnt_vpd.timeout)
{
nnt_vpd.timeout = vpd_default_timeout;
}
error = vpd_write(&nnt_vpd, nnt_device);
if (error)
{
goto ReturnOnFinished;
}
mst_vpd_write.offset = nnt_vpd.offset;
mst_vpd_write.data = nnt_vpd.data;
/* Copy the data to the user space. */
if (copy_to_user(user_buffer, &mst_vpd_write, sizeof(struct mst_vpd_write4_st)) != 0)
{
error = -EFAULT;
goto ReturnOnFinished;
}
break;
}
case GET_DMA_PAGES:
{
error = dma_pages_ioctl(NNT_GET_DMA_PAGES, user_buffer, nnt_device);
break;
}
case RELEASE_DMA_PAGES:
{
error = dma_pages_ioctl(NNT_RELEASE_DMA_PAGES, user_buffer, nnt_device);
break;
}
case READ_DWORD_FROM_CONFIG_SPACE:
{
struct nnt_read_dword_from_config_space nnt_read_from_cspace = {0};
/* Copy the request from user space. */
if (copy_from_user(&nnt_read_from_cspace, user_buffer, sizeof(struct nnt_read_dword_from_config_space)))
{
error = -EFAULT;
goto ReturnOnFinished;
}
/* Read the dword. */
if (read_dword(&nnt_read_from_cspace, nnt_device))
{
goto ReturnOnFinished;
}
/* Copy the data to the user space. */
if (copy_to_user(user_buffer, &nnt_read_from_cspace, sizeof(struct nnt_read_dword_from_config_space)) != 0)
{
error = -EFAULT;
goto ReturnOnFinished;
}
break;
}
case MST_META_DATA:
{
struct mst_meta_data meta_data;
struct mst_hdr hdr;
memset(&meta_data, 0, sizeof(meta_data));
/* Copy the request from user space. */
if (copy_from_user(&hdr, user_buffer, sizeof(struct mst_hdr)))
{
error = -EFAULT;
goto ReturnOnFinished;
}
if (hdr.payload_version_major != MST_META_DATA_VERSION_MAJOR || hdr.payload_len < sizeof(meta_data.data))
{
error = -EINVAL;
goto ReturnOnFinished;
}
// fill meta_data hdr
meta_data.hdr.hdr_version = MST_HDR_VERSION;
meta_data.hdr.hdr_len = sizeof(meta_data.hdr);
meta_data.hdr.payload_len = sizeof(meta_data.data);
meta_data.hdr.payload_version_major = MST_META_DATA_VERSION_MAJOR;
meta_data.hdr.payload_version_minor = MST_META_DATA_VERSION_MINOR;
// fill payload
meta_data.data.api_version_major = MST_API_VERSION_MAJOR;
meta_data.data.api_version_minor = MST_API_VERSION_MINOR;
/* Copy the data to the user space. */
if (copy_to_user(user_buffer, &meta_data, sizeof(meta_data)) != 0)
{
error = -EFAULT;
goto ReturnOnFinished;
}
break;
}
case MST_PARAMS:
{
struct nnt_device_parameters nnt_parameters;
struct mst_params_st mst_params;
error = get_nnt_device_parameters(&nnt_parameters, nnt_device);
if (error)
{
goto ReturnOnFinished;
}
mst_params.bus = nnt_parameters.bus;
mst_params.bar = 0;
mst_params.domain = nnt_parameters.domain;
mst_params.func = nnt_parameters.function;
mst_params.slot = nnt_parameters.slot;
mst_params.device = nnt_parameters.device;
mst_params.vendor = nnt_parameters.vendor;
mst_params.subsystem_device = nnt_parameters.subsystem_device;
mst_params.subsystem_vendor = nnt_parameters.subsystem_vendor;
mst_params.vendor_specific_cap = nnt_parameters.vendor_specific_capability;
mst_params.multifunction = nnt_parameters.multifunction;
mst_params.vsec_cap_mask = nnt_parameters.vsec_capability_mask;
/* Copy the data to the user space. */
if (copy_to_user(user_buffer, &mst_params, sizeof(struct mst_params_st)) != 0)
{
error = -EFAULT;
goto ReturnOnFinished;
}
break;
}
case STOP:
{
error = destroy_nnt_device_bc(nnt_device);
break;
}
case PCICONF_DMA_PROPS:
case PCICONF_MEM_ACCESS:
case MODIFY:
break;
default:
error = -EINVAL;
break;
}
ReturnOnFinished:
mutex_unlock_nnt(file);
return error;
}
struct file_operations fop = {.unlocked_ioctl = ioctl,
.open = mst_pciconf_bc_open,
.write = mst_pciconf_bc_write,
.read = mst_pciconf_bc_read,
.owner = THIS_MODULE};
int with_unknown = 0;
module_param(with_unknown, int, S_IRUSR | S_IWUSR);
static int __init mst_pciconf_init_module(void)
{
dev_t device_number = -1;
int is_alloc_chrdev_region = 0;
int error = 0;
/* Allocate char driver region and assign major number */
major_number = register_chrdev(0, name, &fop);
if (major_number <= 0)
{
printk(KERN_ERR "Unable to register character mst pciconf driver.\n");
error = -EINVAL;
}
/* Create device files for MFT. */
error = create_nnt_devices(device_number, is_alloc_chrdev_region, &fop, NNT_PCICONF_DEVICES,
NNT_MELLANOX_PCI_VENDOR, with_unknown) ||
create_nnt_devices(device_number, is_alloc_chrdev_region, &fop, NNT_PCI_DEVICES, NNT_NVIDIA_PCI_VENDOR,
with_unknown);
return error;
}
static void __exit mst_pciconf_cleanup_module(void)
{
int is_alloc_chrdev_region = 0;
destroy_nnt_devices(is_alloc_chrdev_region);
unregister_chrdev(major_number, name);
}
module_init(mst_pciconf_init_module);
module_exit(mst_pciconf_cleanup_module);

View File

@@ -0,0 +1,193 @@
#ifndef MST_PCICONF_H
#define MST_PCICONF_H
/* These will be specific for PCI CONF*/
#define PCICONF_MAGIC 0xD2
#define PCICONF_MAX_BUFFER_SIZE 256
#define PCICONF_MAX_MEMACCESS_SIZE 1024
#define PCICONF_MAX_PAGES_SIZE 8
#define PCICONF_CAP_VEC_LEN 16
/* Versions */
#define MST_HDR_VERSION 1
#define MST_META_DATA_VERSION_MAJOR 1
#define MST_META_DATA_VERSION_MINOR 0
#define MST_API_VERSION_MAJOR 1
#define MST_API_VERSION_MINOR 0
/* Common Structs*/
struct mst_hdr {
unsigned short hdr_version;
unsigned short hdr_len;
unsigned short payload_version_major;
unsigned int payload_version_minor;
unsigned int payload_len;
};
#define PCICONF_INIT _IOC(_IOC_NONE,PCICONF_MAGIC,0,sizeof(struct mst_pciconf_init_st))
struct mst_pciconf_init_st {
unsigned int domain;
unsigned int bus;
unsigned int devfn;
/* Byte offsets in configuration space */
unsigned int addr_reg;
unsigned int data_reg;
};
#define PCICONF_STOP _IOC (_IOC_NONE,PCICONF_MAGIC,1,0)
#define PCICONF_READ4 _IOR (PCICONF_MAGIC,1,struct mst_read4_st)
struct mst_read4_st {
unsigned int offset;
unsigned int data; /*OUT*/
};
#define PCICONF_WRITE4 _IOW (PCICONF_MAGIC,2,struct mst_write4_st)
struct mst_write4_st {
unsigned int offset;
unsigned int data;
};
#define PCICONF_MODIFY _IOWR(PCICONF_MAGIC,3,struct mst_modify_st)
struct mst_modify_st {
unsigned int address_space;
unsigned int offset;
unsigned int data;
unsigned int mask;
unsigned int old_data; /*OUT*/
};
#define PCICONF_READ4_BUFFER _IOR (PCICONF_MAGIC,4,struct mst_read4_st)
#define PCICONF_READ4_BUFFER_EX _IOR (PCICONF_MAGIC,4,struct mst_read4_buffer_st)
struct mst_read4_buffer_st {
unsigned int address_space;
unsigned int offset;
int size;
unsigned int data[PCICONF_MAX_BUFFER_SIZE/4]; /*OUT*/
};
#define PCICONF_WRITE4_BUFFER _IOW (PCICONF_MAGIC,5,struct mst_write4_buffer_st)
struct mst_write4_buffer_st {
unsigned int address_space;
unsigned int offset;
int size;
unsigned int data[PCICONF_MAX_BUFFER_SIZE/4]; /*IN*/
};
#define PCICONF_MST_PARAMS _IOR (PCICONF_MAGIC,6,struct mst_params_st)
struct mst_params_st {
unsigned int domain;
unsigned int bus;
unsigned int slot;
unsigned int func;
unsigned int bar;
unsigned int device;
unsigned int vendor;
unsigned int subsystem_device;
unsigned int subsystem_vendor;
unsigned int vendor_specific_cap;
u_int32_t vsec_cap_mask;
unsigned int multifunction;
};
#define PCICONF_READ4_NEW _IOR (PCICONF_MAGIC,7,struct mst_read4_new_st)
struct mst_read4_new_st {
unsigned int address_space;
unsigned int offset;
unsigned int data; /*OUT*/
};
/****************************************************/
/* VPD ACCESS */
#define PCICONF_VPD_READ4 _IOR(PCICONF_MAGIC, 7, struct mst_vpd_read4_st)
struct mst_vpd_read4_st {
unsigned int offset; /* IN - must be aligned to DWORD */
unsigned int data; /* OUT */
};
#define PCICONF_WRITE4_NEW _IOW (PCICONF_MAGIC,8,struct mst_write4_new_st)
struct mst_write4_new_st {
unsigned int address_space;
unsigned int offset;
unsigned int data;
};
#define PCICONF_VPD_WRITE4 _IOW(PCICONF_MAGIC, 8, struct mst_vpd_write4_st)
struct mst_vpd_write4_st {
unsigned int offset; /* IN - must be aligned to DWORD */
unsigned int data; /* IN */
};
/*
* MEM_ACCESS
*/
typedef enum {
MST_DMA_ICMD,
MST_DMA_END=32
} mst_dma_type_t;
#define PCICONF_MEM_ACCESS _IOWR(PCICONF_MAGIC, 10, struct mst_mem_access_st)
struct mst_mem_access_st {
mst_dma_type_t mem_type;
unsigned int _rw; /* READ: 0, WRITE: 1 */
unsigned int offset;
unsigned int size;
unsigned char data[PCICONF_MAX_MEMACCESS_SIZE];
};
#define PCICONF_DMA_PROPS _IOR (PCICONF_MAGIC, 11, struct mst_dma_props_st)
struct dma_prop {
unsigned long long int dma_pa;
unsigned int mem_size;
};
struct mst_dma_props_st {
struct dma_prop dma_props[MST_DMA_END];
};
#define PCICONF_MST_META_DATA _IOR (PCICONF_MAGIC, 12, struct mst_meta_data)
struct mst_meta_data_payload {
unsigned short api_version_major;
unsigned int api_version_minor;
unsigned int cap_vector[PCICONF_CAP_VEC_LEN];
};
struct mst_meta_data {
struct mst_hdr hdr;
struct mst_meta_data_payload data;
};
#define PCICONF_GET_DMA_PAGES _IOR (PCICONF_MAGIC, 13, struct page_info_st)
#define PCICONF_RELEASE_DMA_PAGES _IOR (PCICONF_MAGIC, 14, struct page_info_st)
struct page_address_st {
u_int64_t dma_address;
u_int64_t virtual_address;
};
struct page_info_st {
unsigned int page_amount;
unsigned long page_pointer_start;
struct page_address_st page_address_array[PCICONF_MAX_PAGES_SIZE];
};
#define PCICONF_READ_DWORD_FROM_CONFIG_SPACE _IOR (PCICONF_MAGIC, 15, struct read_dword_from_config_space)
struct read_dword_from_config_space {
unsigned int offset;
unsigned int data;
};
#endif

View File

@@ -0,0 +1,32 @@
KPVER ?= $(shell uname -r)
KSRC ?= /lib/modules/$(KPVER)/build
CPU_ARCH ?= $(shell uname -m)
# Oracle Linux OS.
ifneq ($(shell if (echo $(KPVER) | grep -qE 'uek'); then \
echo "YES"; else echo ""; fi),)
override WITH_MAKE_PARAMS += ctf-dir=$(CWD)/.ctf
endif
NNT_DRIVER_LOCATION = ../../nnt_driver
PACKAGE_NAME = nnt-driver
PACKAGE_VERSION = 1.0.0
PACKAGE_RC = 1
%: %.in
sed \
-e 's/@PACKAGE_NAME@/$(PACKAGE_NAME)/g' \
-e 's/@PACKAGE_VERSION@/$(PACKAGE_VERSION)/g' \
-e 's/@PACKAGE_RC@/$(PACKAGE_RC)/g' \
<$< >$@
ifneq ($(findstring ppc64, $(CPU_ARCH)),)
obj-m += mst_ppc_pci_reset.o
EXTRA_CFLAGS= -I$(PWD)/$(NNT_DRIVER_LOCATION)
endif
all:
make -C $(KSRC) M=$(PWD) CONFIG_CTF= CONFIG_CC_STACKPROTECTOR_STRONG= $(WITH_MAKE_PARAMS) modules
clean:
make -C $(KSRC) M=$(PWD) clean

View File

@@ -0,0 +1,306 @@
#include <linux/pci.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kmod.h>
#include <linux/delay.h>
#include "nnt_ppc_device_list.h"
#include "nnt_ppc_driver_defs.h"
#include "nnt_defs.h"
MODULE_AUTHOR("Itay Avraham <itayavr@nvidia.com>");
MODULE_DESCRIPTION("NNT PPC driver (NVIDIA® networking tools driver)");
MODULE_LICENSE("Dual BSD/GPL");
/* Passing PCI devices (DBDF addresses), separated by comma, for example:
* 0000:00:08.0,0000:00:08.1 */
char pci_device_list[NNT_DEVICE_LIST_SIZE];
/* Create the file in sysfs. */
module_param_string(pci_dev, pci_device_list, sizeof(pci_device_list), 0444);
struct nnt_ppc_reset_info nnt_ppc_reset;
void restore_pci_configuration_space(void)
{
struct nnt_ppc_device* nnt_pci_device;
list_for_each_entry(nnt_pci_device, &nnt_device_list,
entry) {
/* Restore the saved state of a PCI device. */
pci_restore_state(nnt_pci_device->pci_device);
}
}
int wait_for_response(void)
{
unsigned short device_id = NNT_UNKNOWN_DEVICE_ID;
struct nnt_ppc_device* nnt_pci_device;
int polling_counter = 0;
int error = 0;
list_for_each_entry(nnt_pci_device, &nnt_device_list,
entry) {
struct pci_dev* pci_device = nnt_pci_device->pci_device;
/* Device id still unknown ? */
while(device_id != pci_device->device) {
/* 100ms is the minimum time that prevents error logs on
dmesg (device is not ready for PCI configuration cycles). */
msleep(NNT_MINIMUM_WAITING_TIME);
/* Read the device id.
Access can fail (if device is not ready) and
as a result we might get errors in dmesg. */
pci_read_config_word(pci_device, PCI_DEVICE_ID,
&device_id);
/* Polling counter violation. */
if (polling_counter > NNT_MAXIMUM_POLLING_NUMBER) {
printk(KERN_ERR "%s Polling on device id failed: reached max value of polling failures for device: %s\n",
dev_driver_string(&pci_device->dev), dev_name(&pci_device->dev));
error = -EINVAL;
goto ReturnOnFinished;
}
polling_counter++;
}
}
ReturnOnFinished:
return error;
}
int set_reset_state(enum pcie_reset_state state)
{
struct nnt_ppc_device* nnt_pci_device;
int error = 0;
list_for_each_entry(nnt_pci_device, &nnt_device_list,
entry) {
struct pci_dev* pci_device = nnt_pci_device->pci_device;
if (PCI_FUNC(pci_device->devfn) == 0) {
/* Set reset state for device devce. */
printk(KERN_DEBUG "%s Send hot reset to the device: %s\n",dev_driver_string(&pci_device->dev), dev_name(&pci_device->dev));
error = pci_set_pcie_reset_state(pci_device, state);
if (error) {
printk(KERN_ERR "%s Set reset state for device failed for device: %s - error: %d\n",
dev_driver_string(&pci_device->dev), dev_name(&pci_device->dev), error);
goto ReturnOnFinished;
}
}
}
ReturnOnFinished:
return error;
}
int save_pci_configucation_space(void)
{
struct nnt_ppc_device* nnt_pci_device = NULL;
int error = 0;
list_for_each_entry(nnt_pci_device, &nnt_device_list,
entry) {
struct pci_dev* pci_device = nnt_pci_device->pci_device;
/* Initialize device before it's used by a driver. */
error = pci_enable_device(pci_device);
if (error) {
printk(KERN_ERR "%s Reset failed for device: %s - error: %d\n",
dev_driver_string(&pci_device->dev), dev_name(&pci_device->dev), error);
goto ReturnOnFinished;
}
/* Enables bus-mastering for device device. */
pci_set_master(pci_device);
/* Save the PCI configuration space of a device before sending hot reset. */
error = pci_save_state(pci_device);
if (error) {
printk(KERN_ERR "%s Reset failed for device: %s - error: %d\n",
dev_driver_string(&pci_device->dev), dev_name(&pci_device->dev), error);
goto ReturnOnFinished;
}
}
ReturnOnFinished:
return error;
}
int pci_devices_reset(void)
{
int error = 0;
if (nnt_ppc_reset.reset_was_done) {
goto ReturnOnFinished;
}
/* Save configuration space for all devices. */
error = save_pci_configucation_space();
CHECK_ERROR(error);
/* Disable the link by sending the hot reset. */
error = set_reset_state(pcie_hot_reset);
CHECK_ERROR(error);
msleep(jiffies_to_msecs(HZ));
/* Enable the link by sending the hot reset. */
error = set_reset_state(pcie_deassert_reset);
CHECK_ERROR(error);
/* Wait for the device to response to PCI configuration cycles. */
error = wait_for_response();
CHECK_ERROR(error);
/* Restore PCI configuration space for all PCI devices. */
restore_pci_configuration_space();
nnt_ppc_reset.reset_was_done = 1;
ReturnOnFinished:
return error;
}
static int init_pci_device(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct nnt_ppc_device* nnt_pci_device;
list_for_each_entry(nnt_pci_device, &nnt_device_list,
entry) {
if (!strcmp(nnt_pci_device->pci_device_dbdf_name, dev_name(&pdev->dev))) {
nnt_pci_device->pci_device = pdev;
nnt_ppc_reset.number_of_found_pci_device++;
}
}
if (nnt_ppc_reset.number_of_requested_pci_device == nnt_ppc_reset.number_of_found_pci_device) {
return pci_devices_reset();
}
return 0;
}
static void remove_pci_device(struct pci_dev *pdev)
{
struct nnt_ppc_device* nnt_pci_device;
list_for_each_entry(nnt_pci_device, &nnt_device_list,
entry) {
if (!strcmp(nnt_pci_device->pci_device_dbdf_name, dev_name(&pdev->dev))) {
pci_clear_master(pdev);
pci_disable_device(pdev);
return;
}
}
}
int ppc_device_structure_init(struct nnt_ppc_device** nnt_pci_device, unsigned int pci_device_name_length)
{
/* Allocate nnt device structure. */
*nnt_pci_device=
kzalloc(sizeof(struct nnt_ppc_device),GFP_KERNEL);
if (!(*nnt_pci_device)) {
return -ENOMEM;
}
/* initialize nnt structure. */
memset(*nnt_pci_device, 0, sizeof(struct nnt_ppc_device));
(*nnt_pci_device)->pci_device_dbdf_name =
kzalloc(pci_device_name_length,GFP_KERNEL);
if (!(*nnt_pci_device)->pci_device_dbdf_name) {
return -ENOMEM;
}
return 0;
}
int parse_pci_devices_string(void)
{
struct nnt_ppc_device* nnt_pci_device;
char buffer[NNT_DEVICE_LIST_SIZE];
char* pci_device_dbdf_name = NULL;
char* dbdf_list = NULL;
int error;
strncpy(buffer, pci_device_list, NNT_DEVICE_LIST_SIZE);
dbdf_list = buffer;
/* Add the pci device name (DBDF) to the list. */
while ((pci_device_dbdf_name = strsep(&dbdf_list, ",")) != NULL) {
/* Allocate ppc device info structure. */
unsigned int pci_device_name_length = strlen(pci_device_dbdf_name);
nnt_pci_device = NULL;
error = ppc_device_structure_init(&nnt_pci_device, pci_device_name_length);
CHECK_ERROR(error);
/* Copy the device name string. */
strncpy(nnt_pci_device->pci_device_dbdf_name, pci_device_dbdf_name,
pci_device_name_length);
/* Create a device entry in the list. */
list_add_tail(&nnt_pci_device->entry, &nnt_device_list);
nnt_ppc_reset.number_of_requested_pci_device++;
}
ReturnOnFinished:
return error;
}
void init_members(void)
{
memset(&nnt_ppc_reset, 0, sizeof(struct nnt_ppc_reset_info));
}
static struct pci_driver nnt_ppc_driver = {
.name = "nnt_ppc_driver",
.id_table = pciconf_devices,
.probe = init_pci_device,
.remove = remove_pci_device,
};
static int __init init(void)
{
int error;
init_members();
/* Parse the parameters from the user space. */
error = parse_pci_devices_string();
CHECK_ERROR(error);
/* Register the NNT PPC driver. */
return pci_register_driver(&nnt_ppc_driver);
ReturnOnFinished:
return error;
}
static void __exit cleanup(void)
{
/* Unregister the NNT PPC driver. */
pci_unregister_driver(&nnt_ppc_driver);
}
module_init(init);
module_exit(cleanup);