blob: decf7a7e5a2a553b193687b26e9efd6caf9615cb [file] [log] [blame]
/*
*
* Copyright (c) International Business Machines Corp., 2001
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
* the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* This test module is for executing and testing
* the kernel code from drivers/base. This module
* is driven by a user space program through
* calls to the ioctl
*
* author: Sean Ruyle
* date: 07/14/2003
*
* module: tbase
*/
#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/device.h>
#include <linux/pci.h>
#include <linux/sysdev.h>
#include <asm/uaccess.h>
#include "tbase.h"
#include "str_mod.h"
MODULE_AUTHOR("Sean Ruyle <srruyle@us.ibm.com>");
MODULE_DESCRIPTION(TMOD_DRIVER_NAME);
MODULE_LICENSE("GPL");
static int tbase_ioctl(struct inode *, struct file *, unsigned int,
unsigned long);
static int tbase_open(struct inode *, struct file *);
static int tbase_close(struct inode *, struct file *);
static int test_device_register(void);
static int test_device_unregister(void);
static int test_bus_add(void);
static int test_get_drv(void);
static int test_put_drv(void);
static int test_reg_firm(void);
static int test_create_file(void);
static int test_dev_suspend(void);
static int test_dev_file(void);
static int test_bus_rescan(void);
static int test_bus_file(void);
static int test_class_reg(void);
static int test_class_get(void);
static int test_class_file(void);
static int test_classdev_reg(void);
static int test_classint_reg(void);
static int test_sysdev_cls_reg(void);
static int test_sysdev_reg(void);
static int Major = TBASEMAJOR;
static ltpmod_user_t ltp_mod;
/*
* File operations struct, to use operations find the
* correct file descriptor
*/
static struct file_operations tbase_fops = {
open : tbase_open,
release: tbase_close,
ioctl: tbase_ioctl,
};
static int tbase_open(struct inode *ino, struct file *f) {
return 0;
}
static int tbase_close(struct inode *ino, struct file *f) {
return 0;
}
/* my bus stuff */
struct device_driver test_driver;
struct device test_device;
static int test_device_match (struct device *dev, struct device_driver *drv) {
printk("tbase: driver is %s\n", drv->name);
// printk("tbase: device is %s\n", dev->name);
if (drv == &test_driver && dev == &test_device) {
printk("tbase: match\n");
return 1;
}
else {
printk("tbase: no match\n");
return 0;
}
}
struct bus_type test_bus_type = {
.name = "test_bus",
.match = test_device_match,
};
/* my driver stuff */
int test_dev_probe(struct device *dev) {
printk("tbase: Entered test_dev_probe\n");
return 0;
}
int test_dev_remove(struct device *dev) {
printk("tbase: Entered test_dev_remove\n");
return 0;
}
struct device_driver test_driver = {
.name = "TestDriver",
.bus = &test_bus_type,
.probe = test_dev_probe,
.remove = test_dev_remove,
};
/* my device stuff */
struct device test_device = {
// .name = "TestDevice",
.bus = &test_bus_type,
.bus_id = "test_bus",
};
/* my class stuff */
static void test_class_release(struct class_device *class_dev) {
printk("tbase: Entered test_class_release\n");
}
int test_class_hotplug(struct class_device *dev, char **envp,
int num_envp, char *buffer, int buffer_size) {
printk("tbase: Entered test_class_hotplug\n");
return 0;
}
struct class test_class = {
.name = "TestClass",
.hotplug = test_class_hotplug,
.release = test_class_release,
};
/* my class device stuff */
struct class_device test_class_dev = {
.class_id = "test_bus",
.dev = &test_device,
.class = &test_class,
};
/* my class interface stuff */
int test_intf_add(struct class_device *class_dev) {
printk("tbase: Entered test_intf_add for the test class_interface\n");
return 0;
}
void test_intf_rem(struct class_device *class_dev) {
printk("tbase: Entered test_intf_rem for the test class interface\n");
}
struct class_interface test_interface = {
.class = &test_class,
.add = &test_intf_add,
.remove = &test_intf_rem,
};
/* my sys_device stuff */
int test_resume(struct sys_device *dev) {
printk("tbase: Entered test resume for sys device\n");
return 0;
}
struct sysdev_class test_sysclass = {
set_kset_name("TestSysclass"),
.resume = test_resume,
};
struct sys_device test_sys_device = {
.id = 0,
.cls = &test_sysclass,
};
/* my attribute stuff */
static inline ssize_t
store_new_id(struct device_driver * driver, const char * buf, size_t count) {
printk("tbase: Entered store new id\n");
return count;
}
/* create attribute driver_attr_new_id */
DRIVER_ATTR(new_id, 0200, NULL, store_new_id);
/* create attribute dev_attr_test_id */
DEVICE_ATTR(test_id, S_IRUGO, NULL, NULL);
/* create attribute bus_attr_test_id */
BUS_ATTR(test_id, S_IRUGO, NULL, NULL);
/* create attribute class_attr_test_id */
CLASS_ATTR(test_id, 0644, NULL, NULL);
/* create attribute class_device_attr_test_id */
CLASS_DEVICE_ATTR(test_id, 0644, NULL, NULL);
/*
* tbase_ioctl:
* a user space program can drive the test functions
* through a call to ioctl once the correct file
* descriptor has been attained
*/
static int tbase_ioctl(struct inode *ino, struct file *f,
unsigned int cmd, unsigned long l) {
int rc;
tmod_interface_t tif;
caddr_t *inparms;
caddr_t *outparms;
printk("Enter tbase_ioctl\n");
inparms = NULL;
outparms = NULL;
rc = 0;
/*
* the following calls are used to setup the
* parameters that might need to be passed
* between user and kernel space, using the tif
* pointer that is passed in as the last
* parameter to the ioctl
*
*/
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);
}
}
/*
* Use a switch statement to determine which function
* to call, based on the cmd flag that is specified
* in user space. Pass in inparms or outparms as
* needed
*
*/
switch(cmd) {
case REG_DEVICE: rc = test_device_register(); break;
case UNREG_DEVICE: rc = test_device_unregister(); break;
case BUS_ADD: rc = test_bus_add(); break;
case GET_DRV: rc = test_get_drv(); break;
case PUT_DRV: rc = test_put_drv(); break;
case REG_FIRM: rc = test_reg_firm(); break;
case CREATE_FILE: rc = test_create_file(); break;
case DEV_SUSPEND: rc = test_dev_suspend(); break;
case DEV_FILE: rc = test_dev_file(); break;
case BUS_RESCAN: rc = test_bus_rescan(); break;
case BUS_FILE: rc = test_bus_file(); break;
case CLASS_REG: rc = test_class_reg(); break;
case CLASS_UNREG: class_unregister(&test_class); break;
case CLASS_GET: rc = test_class_get(); break;
case CLASS_FILE: rc = test_class_file(); break;
case CLASSDEV_REG: rc = test_classdev_reg(); break;
case CLASSINT_REG: rc = test_classint_reg(); break;
case SYSDEV_CLS_REG: rc = test_sysdev_cls_reg(); break;
case SYSDEV_CLS_UNREG: sysdev_class_unregister(&test_sysclass); break;
case SYSDEV_REG: rc = test_sysdev_reg(); break;
case SYSDEV_UNREG: sys_device_unregister(&test_sys_device); break;
default:
printk("tbase: Mismatching ioctl command\n");
break;
}
/*
* copy in the test return code, the reason we
* this is so that in user space we can tell the
* difference between an error in one of our test
* calls or an error in the ioctl function
*/
tif.out_rc = rc;
rc = 0;
/*
* setup the rest of tif pointer for returning to
* to user space, using copy_to_user if needed
*/
/* if outparms then copy outparms into tif.out_data */
if (outparms) {
if (copy_to_user(tif.out_data, outparms, tif.out_len)) {
printk("tbase: 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("tbase: Unsuccessful copy_to_user of tif\n");
rc = -EFAULT;
}
/*
* free inparms and outparms
*/
if (inparms) {
kfree(inparms);
}
if (outparms) {
kfree(outparms);
}
return rc;
}
/*
* test_device_register
* makes call to device register passing in
* the device pointer that we found in a previos
* function, returns an error code
*/
static int test_device_register() {
struct device *dev = ltp_mod.dev;
struct device_driver *drv = dev->driver;
/* check if device register returns an error */
if (device_register(dev)) {
printk("tbase: Device not registered\n");
return 1;
}
else
printk("tbase: Device registered\n");
driver_unregister(drv);
/* check if driver_register returns an error */
if (driver_register(drv)) {
printk("tbase: Driver not registered\n");
return 1;
}
else
printk("tbase: Driver registered\n");
return 0;
}
/*
* test_device_unregister
* make test call to device_unregister which
* will in turn make calls that will decrememnt
* the reference count and clean up as required
*/
static int test_device_unregister() {
struct device *dev = ltp_mod.dev;
/* increment reference count */
get_device(dev);
/* reset remove pointer */
if (dev->driver->remove)
dev->driver->remove = NULL;
device_unregister(dev);
//check that reference count is smaller by one
return 0;
}
/*
* test_bus_add
* make call to bus_add_device, which will
* in turn add the device that is passed in
* to the bus
*/
static int test_bus_add() {
/* check if device register returns an error */
if (bus_add_device(&test_device)) {
printk("tbase: Device not added to bus\n");
return 1;
}
else {
printk("tbase: Device added to bus\n");
return 0;
}
}
/*
* test_get_drv
* make test call to get_driver which should
* return a pointer to the driver passed in
* and increase the reference count to that
* kobject
*/
static int test_get_drv() {
int a, rc;
struct device_driver *drv = &test_driver,
*tmp = NULL;
/* get reference count before test call */
a = atomic_read(&drv->kobj.refcount);
/* make test call */
if ((tmp = get_driver(drv))) {
rc = 0;
printk("tbase: get driver returned driver\n");
}
else {
rc = 1;
printk("tbase: get driver failed to return driver\n");
}
/* check reference count */
if ((a == (atomic_read(&drv->kobj.refcount) - 1) )) {
rc = 0;
printk("tbase: correctly set ref count get driver\n");
}
else {
rc = 1;
printk("tbase: incorrect ref count get driver\n");
}
return rc;
}
/*
* test_class_get
* make test call to class_get which should return
* a pointer to the class passed in and increase
* the reference count to that kobject
*/
static int test_class_get() {
int rc;
struct class *tmp = NULL;
/* get reference count before test call */
tmp = class_get(&test_class);
if (tmp == &test_class) {
printk("tbase: Success get class\n");
rc = 0;
}
else {
printk("tbase: Failure get class\n");
rc = 1;
}
class_put(&test_class);
return rc;
}
/*
* test_put_drv
* make test call to put_driver which should
* decrease the reference count to the kobject
* pointer in the driver structure
*/
static int test_put_drv() {
int a, rc;
struct device_driver *drv = &test_driver;
/* get reference count before test call */
a = atomic_read(&drv->kobj.refcount);
/* make test call */
put_driver(drv);
/* check reference count */
if ((a == (atomic_read(&drv->kobj.refcount) + 1) )) {
rc = 0;
printk("tbase: correctly set ref count put driver\n");
}
else {
rc = 1;
printk("tbase: incorrect ref count put driver\n");
}
return rc;
}
/*
* test_reg_firm
* test call to register_firmware, which will
* register the subsystem, takes in a struct
* subsystem pointer, we can use our bus pointer
* that should have been found in a previous test
* to pass in a subsystem pointer, returns an
* error code
*/
static int test_reg_firm() {
struct subsystem *subsys = NULL;
/* check pointer exists */
if (!(subsys = &test_bus_type.subsys)) {
printk("tbase: subsys pointer not set in reg firmware\n");
return 1;
}
/* unregiser firmware */
firmware_unregister(subsys);
/* make test call */
if (firmware_register(subsys)) {
printk("tbase: failed register firmware\n");
return 1;
}
else {
printk("tbase: regsitered firmware\n");
return 0;
}
}
/*
* test_create_file
* make test call to create sysfs file for the
* driver and if that call is successful then
* make a call to remove the file
*/
static int test_create_file() {
struct device_driver *drv = &test_driver;
if (driver_create_file(drv, &driver_attr_new_id)) {
printk("tbase: failed create sysfs file\n");
return 1;
}
else {
printk("tbase: created sysfs file\n");
driver_remove_file(drv, &driver_attr_new_id);
return 0;
}
}
/*
* test_dev_suspend
* make test call to device_suspend and
* if that call is successful then make
* a call to device_resume
*/
static int test_dev_suspend() {
int error = 0;
error = device_suspend(SUSPEND_SAVE_STATE);
if (error)
printk("tbase: Failed on device suspend call\n");
else {
printk("tbase: Successful on device suspend call\n");
device_resume();
}
error = device_suspend(SUSPEND_DISABLE);
if (error)
printk("tbase: Failed on device suspend call\n");
else {
printk("tbase: Successful on device suspend call\n");
device_resume();
}
return error;
}
/*
* test_dev_file
* make test call to device_create_file
* and if that call is successful make
* another call to device_remove_file
*/
static int test_dev_file() {
struct device *dev = &test_device;
if (device_create_file(dev, &dev_attr_test_id)) {
printk("tbase: failed to create dev sysfs file\n");
return 1;
}
else {
printk("tbase: created dev sysfs file\n");
device_remove_file(dev, &dev_attr_test_id);
return 0;
}
}
/*
* test_bus_rescan
* make test call to bus_rescan_devices which
* will rescan the bus and attempt to match devices
* to drivers, will return 0 for no matches or
* the number of matches made, check that the
* value returned is not negative
*/
static int test_bus_rescan() {
int count = 0;
count = bus_rescan_devices(&test_bus_type);
if (count == 0)
printk("tbase: found no device/driver matches\n");
else if (count > 0)
printk("tbase; found match\n");
else {
printk("tbase: bus rescan failed\n");
return count;
}
return 0;
}
/*
* test_bus_file
* make test call to bus_create_file
* and if that call is successful make
* another call to bus_remove_file
*/
static int test_bus_file() {
struct bus_type *bus = &test_bus_type;
if (bus_create_file(bus, &bus_attr_test_id)) {
printk("tbase: failed to create bus sysfs file\n");
return 1;
}
else {
printk("tbase: created bus sysfs file\n");
bus_remove_file(bus, &bus_attr_test_id);
return 0;
}
}
/*
* test_class_file
* make test call to class_create_file
* and if that call is successful make
* another call to class_remove_file
*/
static int test_class_file() {
struct class * cls = &test_class;
if (class_create_file(cls, &class_attr_test_id)) {
printk("tbase: failed to create class sysfs file\n");
return 1;
}
else {
printk("tbase: created class sysfs file\n");
class_remove_file(cls, &class_attr_test_id);
return 0;
}
}
/*
* test_class_reg
* make test call to class_register
* with the test_class that is defined
* in this module, if that call is
* successful then call unregister
*/
static int test_class_reg() {
int error;
error = class_register(&test_class);
if (error)
printk("tbase: class register failed\n");
else
printk("tbase: class register succeeded\n");
return error;
}
/*
* test_classdev_reg
* make test call to class_device_register
* and if that returns successful then
* make call to class_device_unregister
*/
static int test_classdev_reg() {
int rc = 0;
if (class_device_register(&test_class_dev)) {
printk("tbase: Failed to register class device\n");
rc = 1;
}
else {
printk("tbase: Registered class device\n");
/* make class device sysfs file */
if (class_device_create_file(&test_class_dev, &class_device_attr_test_id)) {
rc = 1;
printk("tbase: Failed to create class device sysfs file\n");
}
else {
printk("tbase: Created class device sysfs file\n");
class_device_remove_file(&test_class_dev, &class_device_attr_test_id);
}
class_device_unregister(&test_class_dev);
}
return rc;
}
/*
* test_classint_reg
* make test call to class_interface_register
* and if that returns successfule then
* make call to class_interface_unregister
*/
static int test_classint_reg() {
if (class_interface_register(&test_interface)) {
printk("tbase: Failed to register class interface\n");
return 1;
}
else {
printk("tbase: Registered class interface\n");
class_interface_unregister(&test_interface);
return 0;
}
}
/*
* test_sysdev_cls_reg
* make test call to sysdev_class_register
* to register the test_sysclass pointer
* as a sysdev_class with the system, check
* the return code
*/
static int test_sysdev_cls_reg() {
if (sysdev_class_register(&test_sysclass)) {
printk("tbase: Failed to register sysdev class\n");
return 1;
}
else {
printk("tbase: Registered sysdev class\n");
return 0;
}
}
/*
* test_sysdev_reg
* make test call to sys_device_register
* to register the test_sysdev pointer
* as a sys_device with the system, check
* the return code
*/
static int test_sysdev_reg() {
if (sys_device_register(&test_sys_device)) {
printk("tbase: Failed to register sysdev \n");
return 1;
}
else {
printk("tbase: Registered sysdev \n");
return 0;
}
}
/*
* tbase_init_module
* set the owner of tbase_fops, register the module
* as a char device, and perform any necessary
* initialization
*/
static int tbase_init_module(void) {
int rc;
bus_register(&test_bus_type);
driver_register(&test_driver);
device_register(&test_device);
tbase_fops.owner = THIS_MODULE;
printk("tbase: *** Register device %s **\n", DEVICE_NAME);
rc = register_chrdev(Major, DEVICE_NAME, &tbase_fops);
if (rc < 0) {
printk("tbase: Failed to register device.\n");
return rc;
}
if (Major == 0)
Major = rc;
/* call any other init functions you might use here */
printk("tbase: Registration success.\n");
return 0;
}
/*
* tmod_exit_module
* unregister the device and any necessary
* operations to close devices
*/
static void tbase_exit_module(void) {
int rc;
device_unregister(&test_device);
driver_unregister(&test_driver);
bus_unregister(&test_bus_type);
/* free any pointers still allocated, using kfree*/
rc = unregister_chrdev(Major, DEVICE_NAME);
if (rc < 0)
printk("tbase: unregister failed\n");
else
printk("tbase: unregister success\n");
}
/* specify what that init is run when the module is first
loaded and that exit is run when it is removed */
module_init(tbase_init_module)
module_exit(tbase_exit_module)