blob: 8065f29c92c80feced74587d6c5eb44f510499a0 [file] [log] [blame]
/*!
* @section LICENSE
* (C) Copyright 2013 Bosch Sensortec GmbH All Rights Reserved
*
* This software program is licensed subject to the GNU General
* Public License (GPL).Version 2,June 1991,
* available at http://www.fsf.org/copyleft/gpl.html
*
* @filename bstclass.c
* @date "Wed Feb 19 13:22:52 2014 +0800"
* @id "6d7c0bb"
*
* @brief
* The core code of bst device driver
*/
#include <linux/init.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/random.h>
#include <linux/sched.h>
#include <linux/seq_file.h>
#include <linux/poll.h>
#include <linux/mutex.h>
#include <linux/rcupdate.h>
#include <linux/compiler.h>
#include <linux/compat.h>
#include "bstclass.h"
static LIST_HEAD(bst_dev_list);
/*
* bst_mutex protects access to both bst_dev_list and input_handler_list.
* This also causes bst_[un]register_device and bst_[un]register_handler
* be mutually exclusive which simplifies locking in drivers implementing
* input handlers.
*/
static DEFINE_MUTEX(bst_mutex);
static void bst_dev_release(struct device *device)
{
struct bst_dev *dev = to_bst_dev(device);
if (NULL != dev)
kfree(dev);
module_put(THIS_MODULE);
}
#ifdef CONFIG_PM
static int bst_dev_suspend(struct device *dev)
{
return 0;
}
static int bst_dev_resume(struct device *dev)
{
return 0;
}
static const struct dev_pm_ops bst_dev_pm_ops = {
.suspend = bst_dev_suspend,
.resume = bst_dev_resume,
.poweroff = bst_dev_suspend,
.restore = bst_dev_resume,
};
#endif /* CONFIG_PM */
static const struct attribute_group *bst_dev_attr_groups[] = {
NULL
};
static struct device_type bst_dev_type = {
.groups = bst_dev_attr_groups,
.release = bst_dev_release,
#ifdef CONFIG_PM
.pm = &bst_dev_pm_ops,
#endif
};
static char *bst_devnode(struct device *dev, umode_t *mode)
{
return kasprintf(GFP_KERNEL, "%s", dev_name(dev));
}
struct class bst_class = {
.name = "bst",
.owner = THIS_MODULE,
.devnode = bst_devnode,
.dev_release = bst_dev_release,
};
EXPORT_SYMBOL_GPL(bst_class);
/**
* bst_allocate_device - allocate memory for new input device
*
* Returns prepared struct bst_dev or NULL.
*
* NOTE: Use bst_free_device() to free devices that have not been
* registered; bst_unregister_device() should be used for already
* registered devices.
*/
struct bst_dev *bst_allocate_device(void)
{
struct bst_dev *dev;
dev = kzalloc(sizeof(struct bst_dev), GFP_KERNEL);
if (dev) {
dev->dev.type = &bst_dev_type;
dev->dev.class = &bst_class;
device_initialize(&dev->dev);
mutex_init(&dev->mutex);
INIT_LIST_HEAD(&dev->node);
__module_get(THIS_MODULE);
}
return dev;
}
EXPORT_SYMBOL(bst_allocate_device);
/**
* bst_free_device - free memory occupied by bst_dev structure
* @dev: input device to free
*
* This function should only be used if bst_register_device()
* was not called yet or if it failed. Once device was registered
* use bst_unregister_device() and memory will be freed once last
* reference to the device is dropped.
*
* Device should be allocated by bst_allocate_device().
*
* NOTE: If there are references to the input device then memory
* will not be freed until last reference is dropped.
*/
void bst_free_device(struct bst_dev *dev)
{
if (dev)
bst_put_device(dev);
}
EXPORT_SYMBOL(bst_free_device);
/**
* bst_register_device - register device with input core
* @dev: device to be registered
*
* This function registers device with input core. The device must be
* allocated with bst_allocate_device() and all it's capabilities
* set up before registering.
* If function fails the device must be freed with bst_free_device().
* Once device has been successfully registered it can be unregistered
* with bst_unregister_device(); bst_free_device() should not be
* called in this case.
*/
int bst_register_device(struct bst_dev *dev)
{
const char *path;
int error;
/*
* If delay and period are pre-set by the driver, then autorepeating
* is handled by the driver itself and we don't do it in input.c.
*/
dev_set_name(&dev->dev, dev->name);
error = device_add(&dev->dev);
if (error)
return error;
path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
dev_dbg(&dev->dev, "%s as %s\n",
dev->name ? dev->name : "Unspecified device",
path ? path : "N/A");
kfree(path);
error = mutex_lock_interruptible(&bst_mutex);
if (error) {
device_del(&dev->dev);
return error;
}
list_add_tail(&dev->node, &bst_dev_list);
mutex_unlock(&bst_mutex);
return 0;
}
EXPORT_SYMBOL(bst_register_device);
/**
* bst_unregister_device - unregister previously registered device
* @dev: device to be unregistered
*
* This function unregisters an input device. Once device is unregistered
* the caller should not try to access it as it may get freed at any moment.
*/
void bst_unregister_device(struct bst_dev *dev)
{
int error;
error = mutex_lock_interruptible(&bst_mutex);
list_del_init(&dev->node);
if (!error)
mutex_unlock(&bst_mutex);
device_unregister(&dev->dev);
}
EXPORT_SYMBOL(bst_unregister_device);
static int bst_open_file(struct inode *inode, struct file *file)
{
const struct file_operations *old_fops, *new_fops = NULL;
int err;
/*
* That's _really_ odd. Usually NULL ->open means "nothing special",
* not "no device". Oh, well...
*/
if (!new_fops || !new_fops->open) {
fops_put(new_fops);
err = -ENODEV;
goto out;
}
old_fops = file->f_op;
file->f_op = new_fops;
err = new_fops->open(inode, file);
if (err) {
fops_put(file->f_op);
file->f_op = fops_get(old_fops);
}
fops_put(old_fops);
out:
return err;
}
static const struct file_operations bst_fops = {
.owner = THIS_MODULE,
.open = bst_open_file,
/*.llseek = noop_llseek,*/
};
static int __init bst_init(void)
{
int err;
/*bst class register*/
err = class_register(&bst_class);
if (err) {
pr_err("unable to register bst_dev class\n");
return err;
}
return err;
}
static void __exit bst_exit(void)
{
/*bst class*/
class_unregister(&bst_class);
}
/*subsys_initcall(bst_init);*/
MODULE_AUTHOR("contact@bosch-sensortec.com");
MODULE_DESCRIPTION("BST CLASS CORE");
MODULE_LICENSE("GPL V2");
module_init(bst_init);
module_exit(bst_exit);