linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Christopher Heiny <cheiny@synaptics.com>
To: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Jean Delvare <khali@linux-fr.org>,
	Linux Kernel <linux-kernel@vger.kernel.org>,
	Linux Input <linux-input@vger.kernel.org>,
	Christopher Heiny <cheiny@synaptics.com>,
	Allie Xiong <axiong@synaptics.com>,
	William Manson <wmanson@synaptics.com>,
	Peichen Chang <peichen.chang@synaptics.com>,
	Joerie de Gram <j.de.gram@gmail.com>,
	Wolfram Sang <w.sang@pengutronix.de>,
	Mathieu Poirier <mathieu.poirier@linaro.org>,
	Linus Walleij <linus.walleij@stericsson.com>,
	Naveen Kumar Gaddipati <naveen.gaddipati@stericsson.com>
Subject: [RFC PATCH 5/17] input: rmidev character driver for RMI4 sensors
Date: Fri, 17 Aug 2012 15:17:45 -0700	[thread overview]
Message-ID: <1345241877-16200-6-git-send-email-cheiny@synaptics.com> (raw)
In-Reply-To: <1345241877-16200-1-git-send-email-cheiny@synaptics.com>

Driver for Synaptics touchscreens using RMI4 protocol.

Signed-off-by: Christopher Heiny <cheiny@synaptics.com>

Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Linus Walleij <linus.walleij@stericsson.com>
Cc: Naveen Kumar Gaddipati <naveen.gaddipati@stericsson.com>
Cc: Joeri de Gram <j.de.gram@gmail.com>

Acked-by: Jean Delvare <khali@linux-fr.org>

---

 drivers/input/rmi4/rmi_dev.c |  448 ++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 448 insertions(+), 0 deletions(-)

diff --git a/drivers/input/rmi4/rmi_dev.c b/drivers/input/rmi4/rmi_dev.c
new file mode 100644
index 0000000..080db55
--- /dev/null
+++ b/drivers/input/rmi4/rmi_dev.c
@@ -0,0 +1,448 @@
+/*
+ * Copyright (c) 2011 Synaptics Incorporated
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/syscalls.h>
+
+#include <linux/rmi.h>
+#include "rmi_driver.h"
+
+#define CHAR_DEVICE_NAME "rmi"
+#define DEVICE_CLASS_NAME "rmidev"
+
+#define RMI_CHAR_DEV_TMPBUF_SZ 128
+#define RMI_REG_ADDR_PAGE_SELECT 0xFF
+#define REG_ADDR_LIMIT 0xFFFF
+
+struct rmidev_data {
+	/* mutex for file operation*/
+	struct mutex file_mutex;
+	/* main char dev structure */
+	struct cdev main_dev;
+
+	/* pointer to the corresponding RMI4 device.  We use this to do */
+	/* read, write, etc. */
+	struct rmi_device *rmi_dev;
+	/* reference count */
+	int ref_count;
+
+	struct class *device_class;
+};
+
+/*store dynamically allocated major number of char device*/
+static int rmidev_major_num;
+
+
+static struct class *rmidev_device_class;
+
+
+/* file operations for RMI char device */
+
+/*
+ * rmidev_llseek: - use to setup register address
+ *
+ * @filp: file structure for seek
+ * @off: offset
+ *       if whence == SEEK_SET,
+ *       high 16 bits: page address
+ *       low 16 bits: register address
+ *
+ *       if whence == SEEK_CUR,
+ *       offset from current position
+ *
+ *       if whence == SEEK_END,
+ *       offset from END(0xFFFF)
+ *
+ * @whence: SEEK_SET , SEEK_CUR or SEEK_END
+ */
+static loff_t rmidev_llseek(struct file *filp, loff_t off, int whence)
+{
+	loff_t newpos;
+	struct rmidev_data *data = filp->private_data;
+
+	if (IS_ERR(data)) {
+		pr_err("%s: pointer of char device is invalid", __func__);
+		return -EBADF;
+	}
+
+	mutex_lock(&(data->file_mutex));
+
+	switch (whence) {
+	case SEEK_SET:
+		newpos = off;
+		break;
+
+	case SEEK_CUR:
+		newpos = filp->f_pos + off;
+		break;
+
+	case SEEK_END:
+		newpos = REG_ADDR_LIMIT + off;
+		break;
+
+	default:		/* can't happen */
+		newpos = -EINVAL;
+		goto clean_up;
+	}
+
+	if (newpos < 0 || newpos > REG_ADDR_LIMIT) {
+		dev_err(&data->rmi_dev->dev, "newpos 0x%04x is invalid.\n",
+			(unsigned int)newpos);
+		newpos = -EINVAL;
+		goto clean_up;
+	}
+
+	filp->f_pos = newpos;
+
+clean_up:
+	mutex_unlock(&(data->file_mutex));
+	return newpos;
+}
+
+/*
+ *  rmidev_read: - use to read data from RMI stream
+ *
+ *  @filp: file structure for read
+ *  @buf: user-level buffer pointer
+ *
+ *  @count: number of byte read
+ *  @f_pos: offset (starting register address)
+ *
+ *	@return number of bytes read into user buffer (buf) if succeeds
+ *          negative number if error occurs.
+ */
+static ssize_t rmidev_read(struct file *filp, char __user *buf,
+		size_t count, loff_t *f_pos)
+{
+	struct rmidev_data *data = filp->private_data;
+	ssize_t retval  = 0;
+	unsigned char tmpbuf[count+1];
+
+	/* limit offset to REG_ADDR_LIMIT-1 */
+	if (count > (REG_ADDR_LIMIT - *f_pos))
+		count = REG_ADDR_LIMIT - *f_pos;
+
+	if (count == 0)
+		return 0;
+
+	if (IS_ERR(data)) {
+		pr_err("%s: pointer of char device is invalid", __func__);
+		return -EBADF;
+	}
+
+	mutex_lock(&(data->file_mutex));
+
+	retval = rmi_read_block(data->rmi_dev, *f_pos, tmpbuf, count);
+
+	if (retval < 0)
+		goto clean_up;
+
+	if (copy_to_user(buf, tmpbuf, count))
+		retval = -EFAULT;
+	else
+		*f_pos += retval;
+
+clean_up:
+
+	mutex_unlock(&(data->file_mutex));
+
+	return retval;
+}
+
+/*
+ * rmidev_write: - use to write data into RMI stream
+ *
+ * @filep : file structure for write
+ * @buf: user-level buffer pointer contains data to be written
+ * @count: number of byte be be written
+ * @f_pos: offset (starting register address)
+ *
+ * @return number of bytes written from user buffer (buf) if succeeds
+ *         negative number if error occurs.
+ */
+static ssize_t rmidev_write(struct file *filp, const char __user *buf,
+		size_t count, loff_t *f_pos)
+{
+	struct rmidev_data *data = filp->private_data;
+	ssize_t retval  = 0;
+	unsigned char tmpbuf[count+1];
+
+	/* limit offset to REG_ADDR_LIMIT-1 */
+	if (count > (REG_ADDR_LIMIT - *f_pos))
+		count = REG_ADDR_LIMIT - *f_pos;
+
+	if (count == 0)
+		return 0;
+
+	if (IS_ERR(data)) {
+		pr_err("%s: pointer of char device is invalid", __func__);
+		return -EBADF;
+	}
+
+	if (copy_from_user(tmpbuf, buf, count))
+		return -EFAULT;
+
+	mutex_lock(&(data->file_mutex));
+
+	retval = rmi_write_block(data->rmi_dev, *f_pos, tmpbuf, count);
+
+	if (retval >= 0)
+		*f_pos += count;
+
+	mutex_unlock(&(data->file_mutex));
+
+	return retval;
+}
+
+/*
+ * rmidev_open: - get a new handle for from RMI stream
+ * @inp : inode struture
+ * @filp: file structure for read/write
+ *
+ * @return 0 if succeeds
+ */
+static int rmidev_open(struct inode *inp, struct file *filp)
+{
+	struct rmidev_data *data = container_of(inp->i_cdev,
+			struct rmidev_data, main_dev);
+	int retval = 0;
+
+	filp->private_data = data;
+
+	if (!data->rmi_dev)
+		return -EACCES;
+
+	mutex_lock(&(data->file_mutex));
+	if (data->ref_count < 1)
+		data->ref_count++;
+	else
+		retval = -EACCES;
+
+	mutex_unlock(&(data->file_mutex));
+
+	return retval;
+}
+
+/*
+ *  rmidev_release: - release an existing handle
+ *  @inp: inode structure
+ *  @filp: file structure for read/write
+ *
+ *  @return 0 if succeeds
+ */
+static int rmidev_release(struct inode *inp, struct file *filp)
+{
+	struct rmidev_data *data = container_of(inp->i_cdev,
+			struct rmidev_data, main_dev);
+
+	if (!data->rmi_dev)
+		return -EACCES;
+
+	mutex_lock(&(data->file_mutex));
+
+	data->ref_count--;
+	if (data->ref_count < 0)
+		data->ref_count = 0;
+
+	mutex_unlock(&(data->file_mutex));
+
+	return 0;
+}
+
+static const struct file_operations rmidev_fops = {
+	.owner =    THIS_MODULE,
+	.llseek =   rmidev_llseek,
+	.read =     rmidev_read,
+	.write =    rmidev_write,
+	.open =     rmidev_open,
+	.release =  rmidev_release,
+};
+
+/*
+ * rmidev_device_cleanup - release memory or unregister driver
+ * @rmidev_data: instance data for a particular device.
+ *
+ */
+static void rmidev_device_cleanup(struct rmidev_data *data)
+{
+	dev_t devno;
+
+	/* Get rid of our char dev entries */
+	if (data) {
+		devno = data->main_dev.dev;
+
+		if (data->device_class)
+			device_destroy(data->device_class, devno);
+
+		cdev_del(&data->main_dev);
+		kfree(data);
+
+		/* cleanup_module is never called if registering failed */
+		unregister_chrdev_region(devno, 1);
+		pr_debug("%s: rmidev device is removed\n", __func__);
+	}
+}
+
+/*
+ * rmi_char_devnode - return device permission
+ *
+ * @dev: char device structure
+ * @mode: file permission
+ *
+ */
+static char *rmi_char_devnode(struct device *dev, mode_t *mode)
+{
+	if (!mode)
+		return NULL;
+	/**mode = 0666*/
+	*mode = (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
+
+	return kasprintf(GFP_KERNEL, "rmi/%s", dev_name(dev));
+}
+
+static int rmidev_init_device(struct rmi_char_device *cd)
+{
+	struct rmi_device *rmi_dev = cd->rmi_dev;
+	struct rmidev_data *data;
+	dev_t dev_no;
+	int retval;
+	struct device *device_ptr;
+
+	if (rmidev_major_num) {
+		dev_no = MKDEV(rmidev_major_num, cd->rmi_dev->number);
+		retval = register_chrdev_region(dev_no, 1, CHAR_DEVICE_NAME);
+	} else {
+		retval = alloc_chrdev_region(&dev_no, 0, 1, CHAR_DEVICE_NAME);
+		/* let kernel allocate a major for us */
+		rmidev_major_num = MAJOR(dev_no);
+		dev_info(&rmi_dev->dev, "Major number of rmidev: %d\n",
+				 rmidev_major_num);
+	}
+	if (retval < 0) {
+		dev_err(&rmi_dev->dev,
+			"Failed to get minor dev number %d, code %d.\n",
+			cd->rmi_dev->number, retval);
+		return retval;
+	} else
+		dev_info(&rmi_dev->dev, "Allocated rmidev %d %d.\n",
+			 MAJOR(dev_no), MINOR(dev_no));
+
+	data = kzalloc(sizeof(struct rmidev_data), GFP_KERNEL);
+	if (!data) {
+		dev_err(&rmi_dev->dev, "Failed to allocate rmidev_data.\n");
+		/* unregister the char device region */
+		__unregister_chrdev(rmidev_major_num, MINOR(dev_no), 1,
+				CHAR_DEVICE_NAME);
+		return -ENOMEM;
+	}
+
+	mutex_init(&data->file_mutex);
+
+	data->rmi_dev = cd->rmi_dev;
+	cd->data = data;
+
+	cdev_init(&data->main_dev, &rmidev_fops);
+
+	retval = cdev_add(&data->main_dev, dev_no, 1);
+	if (retval) {
+		dev_err(&cd->rmi_dev->dev, "Error %d adding rmi_char_dev.\n",
+			retval);
+		rmidev_device_cleanup(data);
+		return retval;
+	}
+
+	dev_set_name(&cd->dev, "rmidev%d", MINOR(dev_no));
+	data->device_class = rmidev_device_class;
+	device_ptr = device_create(
+			data->device_class,
+			NULL, dev_no, NULL,
+			CHAR_DEVICE_NAME"%d",
+			MINOR(dev_no));
+
+	if (IS_ERR(device_ptr)) {
+		dev_err(&cd->rmi_dev->dev, "Failed to create rmi device.\n");
+		rmidev_device_cleanup(data);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void rmidev_remove_device(struct rmi_char_device *cd)
+{
+	struct rmidev_data *data;
+
+	dev_dbg(&cd->dev, "%s: removing an rmidev device.\n", __func__);
+	if (!cd)
+		return;
+
+	data = cd->data;
+	if (data)
+		rmidev_device_cleanup(data);
+}
+
+static struct rmi_char_driver rmidev_driver = {
+	.driver = {
+		.name = "rmidev",
+		.owner = THIS_MODULE,
+	},
+
+	.init = rmidev_init_device,
+	.remove = rmidev_remove_device,
+};
+
+static int __init rmidev_init(void)
+{
+	int error = 0;
+	pr_debug("%s: rmi_dev initialization.\n", __func__);
+
+	/* create device node */
+	rmidev_device_class = class_create(THIS_MODULE, DEVICE_CLASS_NAME);
+
+	if (IS_ERR(rmidev_device_class)) {
+		pr_err("%s: ERROR - Failed to create /dev/%s.\n", __func__,
+			CHAR_DEVICE_NAME);
+		return -ENODEV;
+	}
+	/* setup permission */
+	rmidev_device_class->devnode = rmi_char_devnode;
+
+	error = rmi_register_character_driver(&rmidev_driver);
+	if (error)
+		class_destroy(rmidev_device_class);
+	return error;
+}
+
+static void __exit rmidev_exit(void)
+{
+	pr_debug("%s: exiting.\n", __func__);
+	rmi_unregister_character_driver(&rmidev_driver);
+	class_destroy(rmidev_device_class);
+}
+
+module_init(rmidev_init);
+module_exit(rmidev_exit);
+
+MODULE_AUTHOR("Christopher Heiny <cheiny@synaptics.com>");
+MODULE_DESCRIPTION("RMI4 Char Device");
+MODULE_LICENSE("GPL");

  parent reply	other threads:[~2012-08-17 23:21 UTC|newest]

Thread overview: 44+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-08-17 22:17 [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Christopher Heiny
2012-08-17 22:17 ` [RFC PATCH 1/17] input: RMI4 public header file and documentation Christopher Heiny
2012-08-22 19:08   ` Linus Walleij
2012-08-22 21:45     ` Dmitry Torokhov
2012-08-23  0:26       ` Christopher Heiny
2012-08-22 23:35     ` Rafael J. Wysocki
2012-08-17 22:17 ` [RFC PATCH 2/17] input: RMI4 core bus and sensor drivers Christopher Heiny
2012-08-23  8:55   ` Linus Walleij
2012-09-25 23:53     ` Christopher Heiny
2012-09-26 11:39       ` Linus Walleij
2012-08-17 22:17 ` [RFC PATCH 3/17] input: RMI4 physical layer drivers for I2C and SPI Christopher Heiny
2012-08-23 13:21   ` Linus Walleij
2012-08-17 22:17 ` [RFC PATCH 4/17] input: RMI4 configs and makefiles Christopher Heiny
2012-08-27 18:39   ` Linus Walleij
2012-08-17 22:17 ` Christopher Heiny [this message]
2012-08-27 18:49   ` [RFC PATCH 5/17] input: rmidev character driver for RMI4 sensors Linus Walleij
2012-09-05  0:26     ` Christopher Heiny
2012-09-05  8:29       ` Linus Walleij
2012-08-17 22:17 ` [RFC PATCH 6/17] input: RMI4 firmware update Christopher Heiny
2012-08-27 21:01   ` Linus Walleij
2012-08-17 22:17 ` [RFC PATCH 7/17] input: RMI4 F01 device control Christopher Heiny
2012-08-27 21:59   ` Linus Walleij
2012-08-17 22:17 ` [RFC PATCH 8/17] input: RMI4 F09 Built-In Self Test Christopher Heiny
2012-08-27 22:07   ` Linus Walleij
2012-09-05  0:21     ` Christopher Heiny
2012-08-17 22:17 ` [RFC PATCH 9/17] input: RMI4 F11 multitouch sensing Christopher Heiny
2012-08-27 22:50   ` Linus Walleij
2012-08-17 22:17 ` [RFC PATCH 10/17] input: RM4 F17 Pointing sticks Christopher Heiny
2012-08-17 22:17 ` [RFC PATCH 11/17] input: RMI4 F19 capacitive buttons Christopher Heiny
2012-08-17 22:17 ` [RFC PATCH 12/17] input: RMI4 F1A simple " Christopher Heiny
2012-08-17 22:17 ` [RFC PATCH 13/17] input: RMI4 F21 Force sensing Christopher Heiny
2012-08-17 22:17 ` [RFC PATCH 14/17] input: RMI4 F30 GPIO/LED control Christopher Heiny
2012-08-27 22:58   ` Linus Walleij
2012-09-05  0:28     ` Christopher Heiny
2012-08-17 22:17 ` [RFC PATCH 15/17] input: RMI4 F34 device reflash Christopher Heiny
2012-08-17 22:17 ` [RFC PATCH 16/17] input: RMI4 F41 Active pen 2D input Christopher Heiny
2012-08-17 22:17 ` [RFC PATCH 17/17] input: RMI4 F54 analog data reporting Christopher Heiny
2012-08-27 23:01   ` Linus Walleij
2012-09-05  0:38     ` Christopher Heiny
2012-08-22 12:50 ` [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Linus Walleij
2012-08-22 21:29   ` Christopher Heiny
2012-08-27 23:20   ` Christopher Heiny
2012-08-28  0:12     ` Linus Walleij
2012-08-27 23:05 ` Linus Walleij

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1345241877-16200-6-git-send-email-cheiny@synaptics.com \
    --to=cheiny@synaptics.com \
    --cc=axiong@synaptics.com \
    --cc=dmitry.torokhov@gmail.com \
    --cc=j.de.gram@gmail.com \
    --cc=khali@linux-fr.org \
    --cc=linus.walleij@stericsson.com \
    --cc=linux-input@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mathieu.poirier@linaro.org \
    --cc=naveen.gaddipati@stericsson.com \
    --cc=peichen.chang@synaptics.com \
    --cc=w.sang@pengutronix.de \
    --cc=wmanson@synaptics.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).