#include #include #include #include #include #include #include #include #include "mod.h" #include "dev.h" #include "debug.h" #include "sysfs.h" MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Suren A. Chilingaryan "); MODULE_DESCRIPTION("Testing module"); MODULE_VERSION("0.0.1"); int test_minors = TEST_MINORS; module_param(test_minors, int, S_IRUGO); static dev_t test_devno; /**< major number */ static test_dev_t **test_devs = NULL; /**< per-device context */ static DEFINE_SEMAPHORE(test_devs_mtx); /**< mutex protecting creation/destruction of devices */ static struct class *test_class; /**< device class */ static void test_module_destroy_cdev(test_dev_t *test) { if (test->dev) { test_sysfs_free(test); device_destroy(test_class, test->devno); } cdev_del(&test->cdev); kfree(test); } static int test_module_setup_cdev(void) { int i; int err = 0; dev_t devno; test_dev_t *test; test = kmalloc(sizeof(test_dev_t), GFP_KERNEL); if (!test) { mod_info("Couldn't allocate memory. Device is not created.\n"); return -ENOMEM; } cdev_init(&test->cdev, test_get_fops()); test->cdev.owner = THIS_MODULE; test->cdev.ops = test_get_fops(); down(&test_devs_mtx); for (i = 0; i < test_minors; i++) { if (!test_devs[i]) break; } if (i == test_minors) { mod_info("No free minor numbers left"); err = -EBUSY; goto fail; } devno = MKDEV(MAJOR(test_devno), MINOR(test_devno) + i); test->devno = devno; err = cdev_add(&test->cdev, devno, 1); if (err) { mod_info("Error %d adding device kmm%d", err, i); goto fail; } test->dev = device_create(test_class, NULL, devno, test, TEST_NODE_FMT, MINOR(test_devno) + i); if (!test->dev) { mod_info("Error creating /dev/%s%d\n", TEST_NODE, MINOR(test_devno) + i); goto fail; } dev_set_drvdata(test->dev, test); test_devs[i] = test; up(&test_devs_mtx); err = test_sysfs_init(test); if (err) goto fail_sysfs; mod_info("Device /dev/%s%d added\n", TEST_NODE, MINOR(test_devno) + i); return 0; fail: up(&test_devs_mtx); fail_sysfs: test_module_destroy_cdev(test); return err; } static void test_module_cleanup(void) { int i; if (test_devs) { for (i = 0; i < test_minors; i++) { if (test_devs[i]) test_module_destroy_cdev(test_devs[i]); } kfree(test_devs); } if (test_class) class_destroy(test_class); unregister_chrdev_region(test_devno, test_minors); } static int __init test_module_init(void) { int err; if ((err = alloc_chrdev_region(&test_devno, 0, test_minors, TEST_NODE))) { mod_info("Couldn't allocate chrdev region. Module not loaded.\n"); goto alloc_chrdev_fail; } test_class = class_create(THIS_MODULE, TEST_NODE); if (IS_ERR(test_class)) { mod_info("No sysfs support. Module not loaded.\n"); goto fail; } test_devs = kmalloc(test_minors * sizeof(test_dev_t*), GFP_KERNEL); if (!test_devs) { mod_info("Couldn't allocate memory. Module not loaded.\n"); err = -ENOMEM; } memset(test_devs, 0, test_minors * sizeof(test_dev_t*)); mod_info("Major %d allocated to node '%s'\n", MAJOR(test_devno), TEST_NODE); err = test_module_setup_cdev(); if (err) goto fail; return 0; fail: test_module_cleanup(); alloc_chrdev_fail: return err; } static void __exit test_module_exit(void) { test_module_cleanup(); } module_init(test_module_init); module_exit(test_module_exit);