1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075 |
- /*
- * Synaptics DSX touchscreen driver
- *
- * Copyright (C) 2012-2016 Synaptics Incorporated. All rights reserved.
- *
- * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (C) 2012 Alexandra Chin <[email protected]>
- * Copyright (C) 2012 Scott Lin <[email protected]>
- *
- * 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.
- *
- * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
- * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
- * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
- * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
- * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
- * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
- * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
- * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
- * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
- * DOLLARS.
- */
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/slab.h>
- #include <linux/interrupt.h>
- #include <linux/delay.h>
- #include <linux/input.h>
- #include <linux/signal.h>
- #include <linux/sched.h>
- #include <linux/gpio.h>
- #include <linux/uaccess.h>
- #include <linux/cdev.h>
- #include <linux/sched/signal.h>
- #include <linux/platform_device.h>
- #include <linux/input/synaptics_dsx.h>
- #include "synaptics_dsx_core.h"
- #define CHAR_DEVICE_NAME "rmi"
- #define DEVICE_CLASS_NAME "rmidev"
- #define SYSFS_FOLDER_NAME "rmidev"
- #define DEV_NUMBER 1
- #define REG_ADDR_LIMIT 0xFFFF
- #define RMIDEV_MAJOR_NUM 0
- static ssize_t rmidev_sysfs_data_show(struct file *data_file,
- struct kobject *kobj, struct bin_attribute *attributes,
- char *buf, loff_t pos, size_t count);
- static ssize_t rmidev_sysfs_data_store(struct file *data_file,
- struct kobject *kobj, struct bin_attribute *attributes,
- char *buf, loff_t pos, size_t count);
- static ssize_t rmidev_sysfs_open_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
- static ssize_t rmidev_sysfs_release_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
- static ssize_t rmidev_sysfs_attn_state_show(struct device *dev,
- struct device_attribute *attr, char *buf);
- static ssize_t rmidev_sysfs_pid_show(struct device *dev,
- struct device_attribute *attr, char *buf);
- static ssize_t rmidev_sysfs_pid_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
- static ssize_t rmidev_sysfs_term_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
- static ssize_t rmidev_sysfs_intr_mask_show(struct device *dev,
- struct device_attribute *attr, char *buf);
- static ssize_t rmidev_sysfs_intr_mask_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
- static ssize_t rmidev_sysfs_concurrent_show(struct device *dev,
- struct device_attribute *attr, char *buf);
- static ssize_t rmidev_sysfs_concurrent_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
- struct rmidev_handle {
- dev_t dev_no;
- pid_t pid;
- unsigned char intr_mask;
- unsigned char *tmpbuf;
- unsigned int tmpbuf_size;
- struct device dev;
- struct synaptics_rmi4_data *rmi4_data;
- struct kobject *sysfs_dir;
- struct kernel_siginfo interrupt_signal;
- struct kernel_siginfo terminate_signal;
- struct task_struct *task;
- void *data;
- bool concurrent;
- };
- struct rmidev_data {
- int ref_count;
- struct cdev main_dev;
- struct class *device_class;
- struct mutex file_mutex;
- struct rmidev_handle *rmi_dev;
- };
- static struct bin_attribute attr_data = {
- .attr = {
- .name = "data",
- .mode = 0664,
- },
- .size = 0,
- .read = rmidev_sysfs_data_show,
- .write = rmidev_sysfs_data_store,
- };
- static struct device_attribute attrs[] = {
- __ATTR(open, 0220,
- synaptics_rmi4_show_error,
- rmidev_sysfs_open_store),
- __ATTR(release, 0220,
- synaptics_rmi4_show_error,
- rmidev_sysfs_release_store),
- __ATTR(attn_state, 0444,
- rmidev_sysfs_attn_state_show,
- synaptics_rmi4_store_error),
- __ATTR(pid, 0664,
- rmidev_sysfs_pid_show,
- rmidev_sysfs_pid_store),
- __ATTR(term, 0220,
- synaptics_rmi4_show_error,
- rmidev_sysfs_term_store),
- __ATTR(intr_mask, 0664,
- rmidev_sysfs_intr_mask_show,
- rmidev_sysfs_intr_mask_store),
- __ATTR(concurrent, 0664,
- rmidev_sysfs_concurrent_show,
- rmidev_sysfs_concurrent_store),
- };
- static int rmidev_major_num = RMIDEV_MAJOR_NUM;
- static struct class *rmidev_device_class;
- static struct rmidev_handle *rmidev;
- DECLARE_COMPLETION(rmidev_remove_complete);
- static irqreturn_t rmidev_sysfs_irq(int irq, void *data)
- {
- struct synaptics_rmi4_data *rmi4_data = data;
- sysfs_notify(&rmi4_data->input_dev->dev.kobj,
- SYSFS_FOLDER_NAME, "attn_state");
- return IRQ_HANDLED;
- }
- static int rmidev_sysfs_irq_enable(struct synaptics_rmi4_data *rmi4_data,
- bool enable)
- {
- int retval = 0;
- unsigned char intr_status[MAX_INTR_REGISTERS];
- unsigned long irq_flags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |
- IRQF_ONESHOT;
- mutex_lock(&(rmi4_data->rmi4_irq_enable_mutex));
- if (enable) {
- if (rmi4_data->irq_enabled) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Interrupt already enabled\n",
- __func__);
- goto exit;
- }
- /* Clear interrupts first */
- retval = synaptics_rmi4_reg_read(rmi4_data,
- rmi4_data->f01_data_base_addr + 1,
- intr_status,
- rmi4_data->num_of_intr_regs);
- if (retval < 0)
- goto exit;
- retval = request_threaded_irq(rmi4_data->irq, NULL,
- rmidev_sysfs_irq, irq_flags,
- PLATFORM_DRIVER_NAME, rmi4_data);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to create irq thread\n",
- __func__);
- goto exit;
- }
- rmi4_data->irq_enabled = true;
- } else {
- if (rmi4_data->irq_enabled) {
- disable_irq(rmi4_data->irq);
- free_irq(rmi4_data->irq, rmi4_data);
- rmi4_data->irq_enabled = false;
- }
- }
- exit:
- mutex_unlock(&(rmi4_data->rmi4_irq_enable_mutex));
- return retval;
- }
- static ssize_t rmidev_sysfs_data_show(struct file *data_file,
- struct kobject *kobj, struct bin_attribute *attributes,
- char *buf, loff_t pos, size_t count)
- {
- int retval;
- unsigned char intr_status = 0;
- unsigned int length = (unsigned int)count;
- unsigned short address = (unsigned short)pos;
- struct synaptics_rmi4_fn *fhandler;
- struct synaptics_rmi4_device_info *rmi;
- struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
- rmi = &(rmi4_data->rmi4_mod_info);
- if (length > (REG_ADDR_LIMIT - address)) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Out of register map limit\n",
- __func__);
- return -EINVAL;
- }
- if (length) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- address,
- (unsigned char *)buf,
- length);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read data\n",
- __func__);
- return retval;
- }
- } else {
- return -EINVAL;
- }
- if (!rmidev->concurrent)
- goto exit;
- if (address != rmi4_data->f01_data_base_addr)
- goto exit;
- if (length <= 1)
- goto exit;
- intr_status = buf[1];
- if (!list_empty(&rmi->support_fn_list)) {
- list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
- if (fhandler->num_of_data_sources) {
- if (fhandler->intr_mask & intr_status) {
- rmi4_data->report_touch(rmi4_data,
- fhandler);
- }
- }
- }
- }
- exit:
- return length;
- }
- static ssize_t rmidev_sysfs_data_store(struct file *data_file,
- struct kobject *kobj, struct bin_attribute *attributes,
- char *buf, loff_t pos, size_t count)
- {
- int retval;
- unsigned int length = (unsigned int)count;
- unsigned short address = (unsigned short)pos;
- struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
- if (length > (REG_ADDR_LIMIT - address)) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Out of register map limit\n",
- __func__);
- return -EINVAL;
- }
- if (length) {
- retval = synaptics_rmi4_reg_write(rmi4_data,
- address,
- (unsigned char *)buf,
- length);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to write data\n",
- __func__);
- return retval;
- }
- } else {
- return -EINVAL;
- }
- return length;
- }
- static ssize_t rmidev_sysfs_open_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
- {
- unsigned int input;
- struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
- if (kstrtouint(buf, 10, &input) != 1)
- return -EINVAL;
- if (input != 1)
- return -EINVAL;
- if (rmi4_data->sensor_sleep) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Sensor sleeping\n",
- __func__);
- return -ENODEV;
- }
- rmi4_data->stay_awake = true;
- rmi4_data->irq_enable(rmi4_data, false, false);
- rmidev_sysfs_irq_enable(rmi4_data, true);
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Attention interrupt disabled\n",
- __func__);
- return count;
- }
- static ssize_t rmidev_sysfs_release_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
- {
- unsigned int input;
- struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
- if (kstrtouint(buf, 10, &input) != 1)
- return -EINVAL;
- if (input != 1)
- return -EINVAL;
- rmidev_sysfs_irq_enable(rmi4_data, false);
- rmi4_data->reset_device(rmi4_data, false);
- rmi4_data->stay_awake = false;
- return count;
- }
- static ssize_t rmidev_sysfs_attn_state_show(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- int attn_state;
- struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
- const struct synaptics_dsx_board_data *bdata =
- rmi4_data->hw_if->board_data;
- attn_state = gpio_get_value(bdata->irq_gpio);
- return snprintf(buf, PAGE_SIZE, "%u\n", attn_state);
- }
- static ssize_t rmidev_sysfs_pid_show(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- return snprintf(buf, PAGE_SIZE, "%u\n", rmidev->pid);
- }
- static ssize_t rmidev_sysfs_pid_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
- {
- unsigned int input;
- struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
- if (kstrtouint(buf, 10, &input) != 1)
- return -EINVAL;
- rmidev->pid = input;
- if (rmidev->pid) {
- rmidev->task = pid_task(find_vpid(rmidev->pid), PIDTYPE_PID);
- if (!rmidev->task) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to locate PID of data logging tool\n",
- __func__);
- return -EINVAL;
- }
- }
- return count;
- }
- static ssize_t rmidev_sysfs_term_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
- {
- unsigned int input;
- if (kstrtouint(buf, 10, &input) != 1)
- return -EINVAL;
- if (input != 1)
- return -EINVAL;
- if (rmidev->pid)
- send_sig_info(SIGTERM, &rmidev->terminate_signal, rmidev->task);
- return count;
- }
- static ssize_t rmidev_sysfs_intr_mask_show(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- return snprintf(buf, PAGE_SIZE, "0x%02x\n", rmidev->intr_mask);
- }
- static ssize_t rmidev_sysfs_intr_mask_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
- {
- unsigned int input;
- if (kstrtouint(buf, 10, &input) != 1)
- return -EINVAL;
- rmidev->intr_mask = (unsigned char)input;
- return count;
- }
- static ssize_t rmidev_sysfs_concurrent_show(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- return snprintf(buf, PAGE_SIZE, "%d\n", rmidev->concurrent);
- }
- static ssize_t rmidev_sysfs_concurrent_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
- {
- unsigned int input;
- if (kstrtouint(buf, 10, &input) != 1)
- return -EINVAL;
- rmidev->concurrent = input > 0 ? true : false;
- return count;
- }
- static int rmidev_allocate_buffer(int count)
- {
- if (count + 1 > rmidev->tmpbuf_size) {
- if (rmidev->tmpbuf_size)
- kfree(rmidev->tmpbuf);
- rmidev->tmpbuf = kzalloc(count + 1, GFP_KERNEL);
- if (!rmidev->tmpbuf) {
- dev_err(rmidev->rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for buffer\n",
- __func__);
- rmidev->tmpbuf_size = 0;
- return -ENOMEM;
- }
- rmidev->tmpbuf_size = count + 1;
- }
- return 0;
- }
- /*
- * rmidev_llseek - set register address to access for RMI device
- *
- * @filp: pointer to file structure
- * @off:
- * if whence == SEEK_SET,
- * off: 16-bit RMI register address
- * if whence == SEEK_CUR,
- * off: offset from current position
- * if whence == SEEK_END,
- * off: offset from end position (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 *dev_data = filp->private_data;
- struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
- if (IS_ERR(dev_data)) {
- pr_err("%s: Pointer of char device data is invalid", __func__);
- return -EBADF;
- }
- mutex_lock(&(dev_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:
- newpos = -EINVAL;
- goto clean_up;
- }
- if (newpos < 0 || newpos > REG_ADDR_LIMIT) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: New position 0x%04x is invalid\n",
- __func__, (unsigned int)newpos);
- newpos = -EINVAL;
- goto clean_up;
- }
- filp->f_pos = newpos;
- clean_up:
- mutex_unlock(&(dev_data->file_mutex));
- return newpos;
- }
- /*
- * rmidev_read: read register data from RMI device
- *
- * @filp: pointer to file structure
- * @buf: pointer to user space buffer
- * @count: number of bytes to read
- * @f_pos: starting RMI register address
- */
- static ssize_t rmidev_read(struct file *filp, char __user *buf,
- size_t count, loff_t *f_pos)
- {
- ssize_t retval;
- unsigned char intr_status = 0;
- unsigned short address;
- struct rmidev_data *dev_data = filp->private_data;
- struct synaptics_rmi4_fn *fhandler;
- struct synaptics_rmi4_device_info *rmi;
- struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
- rmi = &(rmi4_data->rmi4_mod_info);
- if (IS_ERR(dev_data)) {
- pr_err("%s: Pointer of char device data is invalid", __func__);
- return -EBADF;
- }
- mutex_lock(&(dev_data->file_mutex));
- if (*f_pos > REG_ADDR_LIMIT) {
- retval = -EFAULT;
- goto clean_up;
- }
- if (count > (REG_ADDR_LIMIT - *f_pos))
- count = REG_ADDR_LIMIT - *f_pos;
- if (count == 0) {
- retval = 0;
- goto clean_up;
- }
- address = (unsigned short)(*f_pos);
- rmidev_allocate_buffer(count);
- retval = synaptics_rmi4_reg_read(rmidev->rmi4_data,
- *f_pos,
- rmidev->tmpbuf,
- count);
- if (retval < 0)
- goto clean_up;
- if (copy_to_user(buf, rmidev->tmpbuf, count))
- retval = -EFAULT;
- else
- *f_pos += retval;
- if (!rmidev->concurrent)
- goto clean_up;
- if (address != rmi4_data->f01_data_base_addr)
- goto clean_up;
- if (count <= 1)
- goto clean_up;
- intr_status = rmidev->tmpbuf[1];
- if (!list_empty(&rmi->support_fn_list)) {
- list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
- if (fhandler->num_of_data_sources) {
- if (fhandler->intr_mask & intr_status) {
- rmi4_data->report_touch(rmi4_data,
- fhandler);
- }
- }
- }
- }
- clean_up:
- mutex_unlock(&(dev_data->file_mutex));
- return retval;
- }
- /*
- * rmidev_write: write register data to RMI device
- *
- * @filp: pointer to file structure
- * @buf: pointer to user space buffer
- * @count: number of bytes to write
- * @f_pos: starting RMI register address
- */
- static ssize_t rmidev_write(struct file *filp, const char __user *buf,
- size_t count, loff_t *f_pos)
- {
- ssize_t retval;
- struct rmidev_data *dev_data = filp->private_data;
- if (IS_ERR(dev_data)) {
- pr_err("%s: Pointer of char device data is invalid", __func__);
- return -EBADF;
- }
- mutex_lock(&(dev_data->file_mutex));
- if (*f_pos > REG_ADDR_LIMIT) {
- retval = -EFAULT;
- goto unlock;
- }
- if (count > (REG_ADDR_LIMIT - *f_pos))
- count = REG_ADDR_LIMIT - *f_pos;
- if (count == 0) {
- retval = 0;
- goto unlock;
- }
- rmidev_allocate_buffer(count);
- if (copy_from_user(rmidev->tmpbuf, buf, count)) {
- retval = -EFAULT;
- goto unlock;
- }
- retval = synaptics_rmi4_reg_write(rmidev->rmi4_data,
- *f_pos,
- rmidev->tmpbuf,
- count);
- if (retval >= 0)
- *f_pos += retval;
- unlock:
- mutex_unlock(&(dev_data->file_mutex));
- return retval;
- }
- static int rmidev_open(struct inode *inp, struct file *filp)
- {
- int retval = 0;
- struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
- struct rmidev_data *dev_data =
- container_of(inp->i_cdev, struct rmidev_data, main_dev);
- if (!dev_data)
- return -EACCES;
- if (rmi4_data->sensor_sleep) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Sensor sleeping\n",
- __func__);
- return -ENODEV;
- }
- rmi4_data->stay_awake = true;
- filp->private_data = dev_data;
- mutex_lock(&(dev_data->file_mutex));
- rmi4_data->irq_enable(rmi4_data, false, false);
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Attention interrupt disabled\n",
- __func__);
- if (dev_data->ref_count < 1)
- dev_data->ref_count++;
- else
- retval = -EACCES;
- mutex_unlock(&(dev_data->file_mutex));
- return retval;
- }
- static int rmidev_release(struct inode *inp, struct file *filp)
- {
- struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
- struct rmidev_data *dev_data =
- container_of(inp->i_cdev, struct rmidev_data, main_dev);
- if (!dev_data)
- return -EACCES;
- mutex_lock(&(dev_data->file_mutex));
- dev_data->ref_count--;
- if (dev_data->ref_count < 0)
- dev_data->ref_count = 0;
- rmi4_data->reset_device(rmi4_data, false);
- rmi4_data->stay_awake = false;
- mutex_unlock(&(dev_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,
- };
- static void rmidev_device_cleanup(struct rmidev_data *dev_data)
- {
- dev_t devno;
- struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
- if (dev_data) {
- devno = dev_data->main_dev.dev;
- if (dev_data->device_class)
- device_destroy(dev_data->device_class, devno);
- cdev_del(&dev_data->main_dev);
- unregister_chrdev_region(devno, 1);
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: rmidev device removed\n",
- __func__);
- }
- }
- static char *rmi_char_devnode(struct device *dev, umode_t *mode)
- {
- if (!mode)
- return NULL;
- *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_create_device_class(void)
- {
- if (rmidev_device_class != NULL)
- return 0;
- rmidev_device_class = class_create(THIS_MODULE, DEVICE_CLASS_NAME);
- if (IS_ERR(rmidev_device_class)) {
- pr_err("%s: Failed to create /dev/%s\n",
- __func__, CHAR_DEVICE_NAME);
- return -ENODEV;
- }
- rmidev_device_class->devnode = rmi_char_devnode;
- return 0;
- }
- static void rmidev_attn(struct synaptics_rmi4_data *rmi4_data,
- unsigned char intr_mask)
- {
- if (!rmidev)
- return;
- if (rmidev->pid && (rmidev->intr_mask & intr_mask))
- send_sig_info(SIGIO, &rmidev->interrupt_signal, rmidev->task);
- return;
- }
- static int rmidev_init_device(struct synaptics_rmi4_data *rmi4_data)
- {
- int retval;
- dev_t dev_no;
- unsigned char attr_count;
- struct rmidev_data *dev_data;
- struct device *device_ptr;
- const struct synaptics_dsx_board_data *bdata =
- rmi4_data->hw_if->board_data;
- if (rmidev) {
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Handle already exists\n",
- __func__);
- return 0;
- }
- rmidev = kzalloc(sizeof(*rmidev), GFP_KERNEL);
- if (!rmidev) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for rmidev\n",
- __func__);
- retval = -ENOMEM;
- goto err_rmidev;
- }
- rmidev->rmi4_data = rmi4_data;
- memset(&rmidev->interrupt_signal, 0, sizeof(rmidev->interrupt_signal));
- rmidev->interrupt_signal.si_signo = SIGIO;
- rmidev->interrupt_signal.si_code = SI_USER;
- memset(&rmidev->terminate_signal, 0, sizeof(rmidev->terminate_signal));
- rmidev->terminate_signal.si_signo = SIGTERM;
- rmidev->terminate_signal.si_code = SI_USER;
- retval = rmidev_create_device_class();
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to create device class\n",
- __func__);
- goto err_device_class;
- }
- if (rmidev_major_num) {
- dev_no = MKDEV(rmidev_major_num, DEV_NUMBER);
- retval = register_chrdev_region(dev_no, 1, CHAR_DEVICE_NAME);
- } else {
- retval = alloc_chrdev_region(&dev_no, 0, 1, CHAR_DEVICE_NAME);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to allocate char device region\n",
- __func__);
- goto err_device_region;
- }
- rmidev_major_num = MAJOR(dev_no);
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Major number of rmidev = %d\n",
- __func__, rmidev_major_num);
- }
- dev_data = kzalloc(sizeof(*dev_data), GFP_KERNEL);
- if (!dev_data) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for dev_data\n",
- __func__);
- retval = -ENOMEM;
- goto err_dev_data;
- }
- mutex_init(&dev_data->file_mutex);
- dev_data->rmi_dev = rmidev;
- rmidev->data = dev_data;
- cdev_init(&dev_data->main_dev, &rmidev_fops);
- retval = cdev_add(&dev_data->main_dev, dev_no, 1);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to add rmi char device\n",
- __func__);
- goto err_char_device;
- }
- dev_set_name(&rmidev->dev, "rmidev%d", MINOR(dev_no));
- dev_data->device_class = rmidev_device_class;
- device_ptr = device_create(dev_data->device_class, NULL, dev_no,
- NULL, CHAR_DEVICE_NAME"%d", MINOR(dev_no));
- if (IS_ERR(device_ptr)) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to create rmi char device\n",
- __func__);
- retval = -ENODEV;
- goto err_char_device;
- }
- retval = gpio_export(bdata->irq_gpio, false);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to export attention gpio\n",
- __func__);
- } else {
- retval = gpio_export_link(&(rmi4_data->input_dev->dev),
- "attn", bdata->irq_gpio);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s Failed to create gpio symlink\n",
- __func__);
- } else {
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Exported attention gpio %d\n",
- __func__, bdata->irq_gpio);
- }
- }
- rmidev->sysfs_dir = kobject_create_and_add(SYSFS_FOLDER_NAME,
- &rmi4_data->input_dev->dev.kobj);
- if (!rmidev->sysfs_dir) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to create sysfs directory\n",
- __func__);
- retval = -ENODEV;
- goto err_sysfs_dir;
- }
- retval = sysfs_create_bin_file(rmidev->sysfs_dir,
- &attr_data);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to create sysfs bin file\n",
- __func__);
- goto err_sysfs_bin;
- }
- for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
- retval = sysfs_create_file(rmidev->sysfs_dir,
- &attrs[attr_count].attr);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to create sysfs attributes\n",
- __func__);
- retval = -ENODEV;
- goto err_sysfs_attrs;
- }
- }
- return 0;
- err_sysfs_attrs:
- for (attr_count--; attr_count >= 0; attr_count--)
- sysfs_remove_file(rmidev->sysfs_dir, &attrs[attr_count].attr);
- sysfs_remove_bin_file(rmidev->sysfs_dir, &attr_data);
- err_sysfs_bin:
- kobject_put(rmidev->sysfs_dir);
- err_sysfs_dir:
- sysfs_remove_link(&(rmi4_data->input_dev->dev.kobj), "attn");
- gpio_unexport(bdata->irq_gpio);
- err_char_device:
- rmidev_device_cleanup(dev_data);
- kfree(dev_data);
- err_dev_data:
- unregister_chrdev_region(dev_no, 1);
- err_device_region:
- if (rmidev_device_class != NULL) {
- class_destroy(rmidev_device_class);
- rmidev_device_class = NULL;
- }
- err_device_class:
- kfree(rmidev);
- rmidev = NULL;
- err_rmidev:
- return retval;
- }
- static void rmidev_remove_device(struct synaptics_rmi4_data *rmi4_data)
- {
- unsigned char attr_count;
- struct rmidev_data *dev_data;
- const struct synaptics_dsx_board_data *bdata =
- rmi4_data->hw_if->board_data;
- if (!rmidev)
- goto exit;
- rmidev_major_num = RMIDEV_MAJOR_NUM;
- for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++)
- sysfs_remove_file(rmidev->sysfs_dir, &attrs[attr_count].attr);
- sysfs_remove_bin_file(rmidev->sysfs_dir, &attr_data);
- kobject_put(rmidev->sysfs_dir);
- sysfs_remove_link(&(rmi4_data->input_dev->dev.kobj), "attn");
- gpio_unexport(bdata->irq_gpio);
- dev_data = rmidev->data;
- if (dev_data) {
- rmidev_device_cleanup(dev_data);
- kfree(dev_data);
- }
- unregister_chrdev_region(rmidev->dev_no, 1);
- if (rmidev_device_class != NULL) {
- class_destroy(rmidev_device_class);
- rmidev_device_class = NULL;
- }
- kfree(rmidev->tmpbuf);
- kfree(rmidev);
- rmidev = NULL;
- exit:
- complete(&rmidev_remove_complete);
- }
- static struct synaptics_rmi4_exp_fn rmidev_module = {
- .fn_type = RMI_DEV,
- .init = rmidev_init_device,
- .remove = rmidev_remove_device,
- .reset = NULL,
- .reinit = NULL,
- .early_suspend = NULL,
- .suspend = NULL,
- .resume = NULL,
- .late_resume = NULL,
- .attn = rmidev_attn,
- };
- static int __init rmidev_module_init(void)
- {
- synaptics_rmi4_new_function(&rmidev_module, true);
- return 0;
- }
- static void __exit rmidev_module_exit(void)
- {
- synaptics_rmi4_new_function(&rmidev_module, false);
- wait_for_completion(&rmidev_remove_complete);
- }
- module_init(rmidev_module_init);
- module_exit(rmidev_module_exit);
- MODULE_AUTHOR("Synaptics, Inc.");
- MODULE_DESCRIPTION("Synaptics DSX RMI Dev Module");
- MODULE_LICENSE("GPL v2");
|