summaryrefslogtreecommitdiff
path: root/drivers/misc/mei/main.c
diff options
context:
space:
mode:
authorAlexander Usyskin <alexander.usyskin@intel.com>2014-06-23 16:10:35 +0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-07-10 01:19:34 +0400
commitf3d8e8788b4efbd13b6e888e85af51385d87d306 (patch)
treead51362c02ae6292c4da2bb7ff88a0d02b225a36 /drivers/misc/mei/main.c
parent7276883f1f98cd0a92fdc049f69bdc0912f7fc16 (diff)
downloadlinux-f3d8e8788b4efbd13b6e888e85af51385d87d306.tar.xz
mei: move from misc to char device
We need to support more then one mei interface hence the simple misc devices is not longer an option In order not break the user space a device with pci function 0 need to be linked to /dev/mei Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com> Signed-off-by: Tomas Winkler <tomas.winkler@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/misc/mei/main.c')
-rw-r--r--drivers/misc/mei/main.c148
1 files changed, 121 insertions, 27 deletions
diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c
index 66f0a1a06451..401a3d526cd0 100644
--- a/drivers/misc/mei/main.c
+++ b/drivers/misc/mei/main.c
@@ -32,7 +32,6 @@
#include <linux/compat.h>
#include <linux/jiffies.h>
#include <linux/interrupt.h>
-#include <linux/miscdevice.h>
#include <linux/mei.h>
@@ -49,19 +48,12 @@
*/
static int mei_open(struct inode *inode, struct file *file)
{
- struct miscdevice *misc = file->private_data;
- struct pci_dev *pdev;
- struct mei_cl *cl;
struct mei_device *dev;
+ struct mei_cl *cl;
int err;
- if (!misc->parent)
- return -ENODEV;
-
- pdev = container_of(misc->parent, struct pci_dev, dev);
-
- dev = pci_get_drvdata(pdev);
+ dev = container_of(inode->i_cdev, struct mei_device, cdev);
if (!dev)
return -ENODEV;
@@ -667,46 +659,148 @@ static const struct file_operations mei_fops = {
.llseek = no_llseek
};
-/*
- * Misc Device Struct
+static struct class *mei_class;
+static dev_t mei_devt;
+#define MEI_MAX_DEVS MINORMASK
+static DEFINE_MUTEX(mei_minor_lock);
+static DEFINE_IDR(mei_idr);
+
+/**
+ * mei_minor_get - obtain next free device minor number
+ *
+ * @dev: device pointer
+ *
+ * returns allocated minor, or -ENOSPC if no free minor left
*/
-static struct miscdevice mei_misc_device = {
- .name = "mei",
- .fops = &mei_fops,
- .minor = MISC_DYNAMIC_MINOR,
-};
+static int mei_minor_get(struct mei_device *dev)
+{
+ int ret;
+
+ mutex_lock(&mei_minor_lock);
+ ret = idr_alloc(&mei_idr, dev, 0, MEI_MAX_DEVS, GFP_KERNEL);
+ if (ret >= 0)
+ dev->minor = ret;
+ else if (ret == -ENOSPC)
+ dev_err(&dev->pdev->dev, "too many mei devices\n");
+ mutex_unlock(&mei_minor_lock);
+ return ret;
+}
-int mei_register(struct mei_device *dev)
+/**
+ * mei_minor_free - mark device minor number as free
+ *
+ * @dev: device pointer
+ */
+static void mei_minor_free(struct mei_device *dev)
{
- int ret;
- mei_misc_device.parent = &dev->pdev->dev;
- ret = misc_register(&mei_misc_device);
- if (ret)
+ mutex_lock(&mei_minor_lock);
+ idr_remove(&mei_idr, dev->minor);
+ mutex_unlock(&mei_minor_lock);
+}
+
+int mei_register(struct mei_device *dev, struct device *parent)
+{
+ struct device *clsdev; /* class device */
+ int ret, devno;
+
+ ret = mei_minor_get(dev);
+ if (ret < 0)
return ret;
- if (mei_dbgfs_register(dev, mei_misc_device.name))
- dev_err(&dev->pdev->dev, "cannot register debugfs\n");
+ /* Fill in the data structures */
+ devno = MKDEV(MAJOR(mei_devt), dev->minor);
+ cdev_init(&dev->cdev, &mei_fops);
+ dev->cdev.owner = mei_fops.owner;
+
+ /* Add the device */
+ ret = cdev_add(&dev->cdev, devno, 1);
+ if (ret) {
+ dev_err(parent, "unable to add device %d:%d\n",
+ MAJOR(mei_devt), dev->minor);
+ goto err_dev_add;
+ }
+
+ clsdev = device_create(mei_class, parent, devno,
+ NULL, "mei%d", dev->minor);
+
+ if (IS_ERR(clsdev)) {
+ dev_err(parent, "unable to create device %d:%d\n",
+ MAJOR(mei_devt), dev->minor);
+ ret = PTR_ERR(clsdev);
+ goto err_dev_create;
+ }
+
+ ret = mei_dbgfs_register(dev, dev_name(clsdev));
+ if (ret) {
+ dev_err(clsdev, "cannot register debugfs ret = %d\n", ret);
+ goto err_dev_dbgfs;
+ }
return 0;
+
+err_dev_dbgfs:
+ device_destroy(mei_class, devno);
+err_dev_create:
+ cdev_del(&dev->cdev);
+err_dev_add:
+ mei_minor_free(dev);
+ return ret;
}
EXPORT_SYMBOL_GPL(mei_register);
void mei_deregister(struct mei_device *dev)
{
+ int devno;
+
+ devno = dev->cdev.dev;
+ cdev_del(&dev->cdev);
+
mei_dbgfs_deregister(dev);
- misc_deregister(&mei_misc_device);
- mei_misc_device.parent = NULL;
+
+ device_destroy(mei_class, devno);
+
+ mei_minor_free(dev);
}
EXPORT_SYMBOL_GPL(mei_deregister);
static int __init mei_init(void)
{
- return mei_cl_bus_init();
+ int ret;
+
+ mei_class = class_create(THIS_MODULE, "mei");
+ if (IS_ERR(mei_class)) {
+ pr_err("couldn't create class\n");
+ ret = PTR_ERR(mei_class);
+ goto err;
+ }
+
+ ret = alloc_chrdev_region(&mei_devt, 0, MEI_MAX_DEVS, "mei");
+ if (ret < 0) {
+ pr_err("unable to allocate char dev region\n");
+ goto err_class;
+ }
+
+ ret = mei_cl_bus_init();
+ if (ret < 0) {
+ pr_err("unable to initialize bus\n");
+ goto err_chrdev;
+ }
+
+ return 0;
+
+err_chrdev:
+ unregister_chrdev_region(mei_devt, MEI_MAX_DEVS);
+err_class:
+ class_destroy(mei_class);
+err:
+ return ret;
}
static void __exit mei_exit(void)
{
+ unregister_chrdev_region(mei_devt, MEI_MAX_DEVS);
+ class_destroy(mei_class);
mei_cl_bus_exit();
}