blob: 1a6be03f8a1959ab94f2ec7fc1e2bf465f5d0419 [file] [log] [blame]
/*
* This pci testing kernel module will allow test calls
* to be driven through various ioctl calls in a
* user space program that has attained the appropriate
* file descriptor for this device. For the functions of
* this module to work correctly there must be a pci
* device somewhere in the system. The tests do not need
* a specific device, and the first pci device available
* will be grabbed.
*
* author: Sean Ruyle (srruyle@us.ibm.com)
* date: 5/20/2003
*
* file: tpci.c,
* module: tpci
*/
#include <linux/config.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/ioctl.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <asm/uaccess.h>
#include "tpci.h"
#include "st_tpci.h"
MODULE_AUTHOR("Sean Ruyle <srruyle@us.ibm.com>");
MODULE_DESCRIPTION(PCI_TEST_DRIVER_NAME);
MODULE_LICENSE("GPL");
static int tpci_ioctl (struct inode *, struct file *, unsigned int, unsigned long);
static int tpci_open (struct inode *, struct file *);
static int tpci_close (struct inode *, struct file *);
static int probe_pci_dev(void);
static int pci_enable(void);
static int pci_disable(void);
static int test_find_bus(void);
static int test_find_class(void);
static int test_find_device(void);
static int test_find_subsys(void);
static int test_scan_bus(void);
static int test_slot_scan(void);
static int test_bus_add_devices(void);
static int test_enable_bridges(void);
static int test_match_device(void);
static int test_reg_driver(void);
static int test_unreg_driver(void);
static int test_assign_resources(void);
static int test_save_state(void);
static int test_restore_state(void);
static int test_max_bus(void);
static int test_find_cap(void);
static int Major = 0;
static tpci_user_t ltp_pci;
/*
* File operations struct, to use operations find the
* correct file descriptor
*/
static struct file_operations tpci_fops = {
open : tpci_open,
release: tpci_close,
ioctl: tpci_ioctl,
};
/*
* open and close operations
*/
static int tpci_open(struct inode *ino, struct file *f) {
return 0;
}
static int tpci_close(struct inode *ino, struct file *f) {
return 0;
}
/*
* tpci_ioctl:
* a user space program can drive the test functions
* through a call to ioctl once the correct file
* descriptor has been attained
*
* calls functions:
*
*/
static int tpci_ioctl(struct inode *ino, struct file *f,
unsigned int cmd, unsigned long l) {
int rc;
struct tpci_interface tif;
caddr_t *inparms;
caddr_t *outparms;
printk("Enter tpci_ioctl\n");
inparms = NULL;
outparms = NULL;
rc = 0;
if (copy_from_user(&tif, (void *)l, sizeof(tif)) ) {
/* Bad address */
return(-EFAULT);
}
/*
* Setup inparms and outparms as needed
*/
if (tif.in_len > 0) {
inparms = (caddr_t *)kmalloc(tif.in_len, GFP_KERNEL);
if(!inparms) {
return(-ENOMEM);
}
rc = copy_from_user(inparms, tif.in_data, tif.in_len);
if(rc) {
kfree(inparms);
return(-EFAULT);
}
}
if (tif.out_len > 0) {
outparms = (caddr_t *)kmalloc(tif.out_len, GFP_KERNEL);
if (!outparms) {
kfree(inparms);
return(-ENOMEM);
}
}
/*
* determine which call to make on the cmd value
*/
switch(cmd) {
case PCI_PROBE: rc = probe_pci_dev(); break;
case PCI_ENABLE: rc = pci_enable(); break;
case PCI_DISABLE: rc = pci_disable(); break;
case FIND_BUS: rc = test_find_bus(); break;
case FIND_CLASS: rc = test_find_class(); break;
case FIND_DEVICE: rc = test_find_device(); break;
case FIND_SUBSYS: rc = test_find_subsys(); break;
case BUS_SCAN: rc = test_scan_bus(); break;
case SLOT_SCAN: rc = test_slot_scan(); break;
case BUS_ADD_DEVICES: rc = test_bus_add_devices(); break;
case ENABLE_BRIDGES: rc = test_enable_bridges(); break;
case MATCH_DEVICE: rc = test_match_device(); break;
case REG_DRIVER: rc = test_reg_driver(); break;
case UNREG_DRIVER: rc = test_unreg_driver(); break;
case PCI_RESOURCES: rc = test_assign_resources(); break;
case SAVE_STATE: rc = test_save_state(); break;
case RESTORE_STATE: rc = test_restore_state(); break;
case TEST_MAX_BUS: rc = test_max_bus(); break;
case FIND_CAP: rc = test_find_cap(); break;
default:
printk("Mismatching ioctl command\n");
break;
}
if(!(ltp_pci.dev))
printk("tpci: After ioctl call dev is NULL\n");
/*
* copy in the return data, and test return code
*/
tif.out_rc = rc;
rc = 0;
/* if outparms then copy outparms into tif.out_data */
if(outparms) {
if(copy_to_user(tif.out_data, outparms, tif.out_len)) {
printk("tpci: Unsuccessful copy_to_user of outparms\n");
rc = -EFAULT;
}
}
/* copy tif structure into l so that can be used by user program */
if(copy_to_user((void*)l, &tif, sizeof(tif)) ) {
printk("tpci: Unsuccessful copy_to_user of tif\n");
rc = -EFAULT;
}
/*
* free inparms and outparms
*/
if (inparms) {
kfree(inparms);
}
if (outparms) {
kfree(outparms);
}
return rc;
}
/*
* probe_pci_dev
* find a pci device that can be used for other test
* calls in this kernel module, select first device
* that finds for use, do not need a specific device
*/
static int probe_pci_dev() {
unsigned int i, j;
struct pci_dev *dev = (struct pci_dev *)kmalloc(sizeof(struct pci_dev),GFP_KERNEL);
struct pci_bus *bus = (struct pci_bus *)kmalloc(sizeof(struct pci_bus),GFP_KERNEL);
/* Zero out the ltp_pci */
memset(&ltp_pci, 0, sizeof(tpci_user_t));
ltp_pci.dev = dev;
ltp_pci.bus = bus;
/* Probe until find a pci device */
for (i = MAX_BUS; i > 0; i--) {
for (j = MAX_DEVFN; j > 1; j--) {
dev = pci_find_slot(i, j);
if(dev && dev->driver) {
printk("tpci: found pci_dev, bus %d, devfn %d\n", i, j);
printk("Slot number: %d\n", dev->devfn );
bus = dev->bus;
printk("Bus number: %d\n", bus->number );
/* copy data into ltp_pci struct */
memcpy(ltp_pci.dev, dev, sizeof(struct pci_dev));
memcpy(ltp_pci.bus, bus, sizeof(struct pci_bus));
return 0;
}
}
}
/* if reaches here did not find a pci device */
printk("tpci: failed to find pci device\n");
return 1;
}
/*
* pci_enable
* enable a pci device so that it may be used in
* later testing in the user test program
*/
static int pci_enable() {
int rc = 0;
struct pci_dev *dev = ltp_pci.dev;
/* check if can enable the device pointer */
if(!dev) {
printk("tpci: dev is NULL\n");
return 1;
}
if( pci_enable_device(dev) ) {
printk("tpci: failed to enable pci device\n");
rc = 1;
}
else {
printk("tpci: enabled pci device\n");
rc = 0;
}
return rc;
}
/*
* pci_disable
* call to pci_disable_device
*/
static int pci_disable() {
int rc = 0;
struct pci_dev *dev = ltp_pci.dev;
/* check if device pointer exists */
if(!dev) {
printk("tpci: dev is NULL\n");
return 1;
}
pci_disable_device(dev);
if (dev->current_state == 4 || dev->current_state == 3) {
printk("tpci: disabled pci device\n");
rc = 0;
}
else {
printk("tpci: failed to disable pci device\n");
rc = 1;
}
return rc;
}
/*
* find_bus
* call to pci_find_bus, use values from bus
* pointer in ltp_pci, make sure that returns
* bus with same values
*/
static int test_find_bus() {
int rc;
int num = ltp_pci.bus->number;
struct pci_bus *temp = NULL;
temp = pci_find_bus(num);
if(!temp) {
printk("tpci: pci_find_bus failed to return bus pointer\n");
rc = 1;
}
else if(temp->number != num) {
printk("tpci: returned bus pointer w/ wrong bus number\n");
rc = 1;
}
else {
printk("tpci: success returned bus pointer \n");
rc = 0;
}
return rc;
}
/*
* find_class
* call to pci_find_class, using values from the
* pci_dev pointer in ltp_pci structure
*/
static int test_find_class() {
int rc;
unsigned int num = ltp_pci.dev->class;
struct pci_dev *temp = NULL;
temp = pci_find_class(num, NULL);
if(!temp) {
printk("tpci: failed to find pci device from class number\n");
rc = 1;
}
else {
printk("tpci: found pci device from class number\n");
rc = 0;
}
return rc;
}
/*
* find_device
* call to pci_find_device, using values for
* parameters from pci_dev pointer in the
* ltp_pci structure
*/
static int test_find_device() {
int rc;
struct pci_dev *temp = NULL;
unsigned short ven = ltp_pci.dev->vendor,
dev = ltp_pci.dev->device;
temp = pci_find_device(ven, dev, NULL);
if(!temp) {
printk("tpci: failed to find pci device from device info\n");
rc = 1;
}
else {
printk("tpci: found pci device from device info\n");
rc = 0;
}
return rc;
}
/*
* find_subsys
* call to pci_find_subsys, use valued from
* pci_dev pointer in ltp_pci structure to
* find pci_dev from subsys info
*/
static int test_find_subsys() {
int rc;
struct pci_dev *temp = NULL;
unsigned short ven = ltp_pci.dev->vendor,
dev = ltp_pci.dev->device,
ss_ven = ltp_pci.dev->subsystem_vendor,
ss_dev = ltp_pci.dev->subsystem_device;
temp = pci_find_subsys(ven, dev, ss_ven, ss_dev, NULL);
if(!temp) {
printk("tpci: failed to find pci device from subsys info\n");
rc = 1;
}
else {
printk("tpci: found pci device from subsys info\n");
rc = 0;
}
return rc;
}
/*
* test_scan_bus
* call to pci_do_scan_bus, which takes
* a struct pci_bus pointer, which will
* return a an integer for how far the
* function got in scanning bus
*/
static int test_scan_bus() {
int rc, num;
struct pci_bus *bus = ltp_pci.bus;
num = pci_do_scan_bus(bus);
/*
* check if returned number is greater than
* max number of bus or less than 0
*/
if(num > MAX_BUS || num < 0) {
printk("tpci: Failed scan bus\n");
rc = 1;
}
else {
printk("tpci: Success scan bus\n");
rc = 0;
}
return rc;
}
/*
* test_slot_scan
* make call to pci_scan_slot, which will
* find the device pointer and setup the
* device info
*/
static int test_slot_scan() {
int rc, ret;
int num = ltp_pci.dev->devfn;
struct pci_bus *bus = ltp_pci.bus;
ret = pci_scan_slot(bus, num);
if(ret > 0) {
printk("tpci: Found device from scan slot\n");
rc = 0;
}
else {
printk("tpci: Failed find device from scan slot\n");
rc = 1;
}
return rc;
}
/*
* test_bus_add_devices
* make call to pci_bus_add_devices,
* which will check the device pointer
* that is passed in for more devices
* that it can add
*/
static int test_bus_add_devices() {
int rc;
struct pci_bus *bus = ltp_pci.bus;
pci_bus_add_devices(bus);
if(bus) {
printk("tpci: Called bus_add_device\n");
rc = 0;
}
else {
printk("tpci: bus_add_device failed\n");
rc = 1;
}
return rc;
}
/*
* test_enable_bridges
* make call to pci_enable_bridges,
* use bus pointer from the ltp_pci
* structure
*/
static int test_enable_bridges() {
int rc;
struct pci_bus *bus = ltp_pci.bus;
pci_enable_bridges(bus);
if(bus) {
printk("tpci: Called enable bridges\n");
rc = 0;
}
else {
printk("tpci: enable_bridges failed\n");
rc = 1;
}
return rc;
}
/*
* test_match_device
* make call to pci_match_device, returns a
* pci_device_id pointer
*/
static int test_match_device() {
int rc;
struct pci_dev *dev = ltp_pci.dev;
struct pci_driver *drv;
const struct pci_device_id *id;
drv = pci_dev_driver(dev);
if(!drv) {
printk("driver pointer not allocated for pci_dev\n");
return 1;
}
id = pci_match_device(drv->id_table, dev);
if(id) {
printk("tpci: Match device success\n");
rc = 0;
}
else {
printk("tpci: Failed return pci_device_id \n");
rc = 1;
}
return rc;
}
/*
* test_reg_driver
* make call to pci_register_driver, which will
* register the driver for a device with the
* system
*/
static int test_reg_driver() {
int rc, ret;
struct pci_driver *drv = (struct pci_driver *)kmalloc(sizeof(struct pci_driver), GFP_KERNEL);
struct pci_driver *tmp = ltp_pci.dev->driver;
/* zero out drv structure */
memset(drv, 0, sizeof(struct pci_driver));
/* copy in structure of tmp, reset some fields */
drv->name = "Tmod_driver";
drv->driver = tmp->driver;
/* copy structure into ltp_pci.drv */
ltp_pci.drv = drv;
memcpy(ltp_pci.drv, drv, sizeof(struct pci_driver));
if(!drv) {
printk("tpci: Device does not have a driver pointer\n");
return 1;
}
ret = pci_register_driver(drv);
if(ret) {
printk("tpci: Success driver register\n");
rc = 0;
}
else {
rc = 1;
printk("tpci: unsuccessful registering pci driver\n");
}
return rc;
}
/*
* test_unreg_driver
* make call to pci_unregister_driver, which will
* unregister the driver for a device from the system
*/
static int test_unreg_driver() {
int rc;
struct pci_driver *drv = ltp_pci.drv;
if(!drv) {
printk("tpci: Device does not have a driver pointer\n");
return 1;
}
pci_unregister_driver(drv);
if(!drv) {
printk("tpci: Unsuccesful driver unregister\n");
rc = 1;
}
else {
printk("tpci: unregistering pci driver\n");
rc = 0;
}
return rc;
}
/*
* test_assign_resources
* make calls to pci_assign_resource, will need
* to setup a dev pointer and resource pointer,
*/
static int test_assign_resources() {
int rc;
struct pci_dev *dev = ltp_pci.dev;
int resno;
for (resno = 0; resno < 7; resno++) {
struct resource *r = dev->resource +resno;
if (r->flags)
pci_assign_resource(dev, resno);
}
/*
* enable device after call to assign resource
* because might error if (!r->start && r->end)
*/
rc = pci_enable_device(dev);
return rc;
}
/*
* test_save_state
* make call to pci_save_state, takes in a u32*
* buffer
*/
static int test_save_state() {
int rc;
u32 *buffer = ltp_pci.state;
struct pci_dev *dev = ltp_pci.dev;
rc = pci_save_state(dev, buffer);
if(rc)
printk("tpci: Failed save state\n");
else
printk("tpci: Saved state of device\n");
return rc;
}
/*
* test_restore_state
* make call to pci_restore_state, get the state buffer
* should have been previously filled out by save state
*/
static int test_restore_state() {
int rc;
u32 *buffer = ltp_pci.state;
struct pci_dev *dev = ltp_pci.dev;
rc = pci_restore_state(dev, buffer);
if(rc)
printk("tpci: Failed restore state\n");
else
printk("tpci: Restored state of device\n");
return rc;
}
/*
* test_max_bus
* make call to pci_max_busnr, which will determine
* the max number of bus on the system
*/
static int test_max_bus() {
int rc, ret;
ret = pci_max_busnr();
if(ret) {
printk("Found max busnr\n");
rc = 0;
}
else {
printk("Did not return max busnr\n");
rc = 1;
}
return rc;
}
/*
* test_find_cap
* make call to pci_find_capability, which
* will determine if a device has a certain
* capability, use second parameter to specify
* which capability you are looking for
*/
static int test_find_cap() {
int rc;
struct pci_dev *dev = ltp_pci.dev;
rc = pci_find_capability(dev, PCI_CAP_ID_PM);
if(rc)
printk("tpci: Does not have tested capability\n");
else
printk("tpci: Device has PM capability\n");
return rc;
}
/*
* tpci_init_module
* set the owner of tpci_fops, register the module
* as a char device, and perform any necessary
* initialization for pci devices
*/
static int tpci_init_module(void) {
int rc;
SET_MODULE_OWNER(&tpci_fops);
rc = register_chrdev(Major, DEVICE_NAME, &tpci_fops);
if (rc < 0) {
printk("tpci: Failed to register device.\n");
return rc;
}
if(Major == 0)
Major = rc;
if(!pci_present())
printk("tpci: pci_present returned false\n");
printk("tpci: Registration success.\n");
return 0;
}
/*
* tpci_exit_module
* unregister the device and any necessary
* operations to close for pci devices
*/
static void tpci_exit_module(void) {
int rc;
kfree(ltp_pci.dev);
kfree(ltp_pci.bus);
kfree(ltp_pci.drv);
rc = unregister_chrdev(Major, DEVICE_NAME);
if (rc < 0)
printk("tpci: unregister failed\n");
else
printk("tpci: unregister success\n");
}
module_init(tpci_init_module)
module_exit(tpci_exit_module)