/* * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 #include #include #include #include #include #include #include #include #include #include "tbase.h" #include "str_mod.h" MODULE_AUTHOR("Sean Ruyle "); 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)