summaryrefslogtreecommitdiffstats
path: root/drivers/misc/nvs/nvs_input.c
blob: d64673d4de689339ff0473f3ced2b81cbdd44c24 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/* Copyright (c) 2016-2017, NVIDIA CORPORATION.  All rights reserved.
 *
 * This software is licensed under the terms of the GNU General Public
 * License version 2, as published by the Free Software Foundation, and
 * may be copied, distributed, and modified under those terms.
 *
 * 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.
 */

#include "nvs_sysfs.h"
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/input.h>

#define NVS_INPUT_DRIVER_VERSION	(1)
#define NVS_INPUT_DRIVER_NAME		"input"

struct nvs_state_input {
	struct input_dev *idev;
	unsigned long input_no;
	unsigned int code_n;
	unsigned int code_n_mod;
};


static int nvs_input_push(struct nvs_state *st)
{
	struct nvs_state_input *si = nvs_st_kif(st);
	unsigned int val;
	unsigned int i;

	for (i = 0; i < si->code_n; i++) {
		memcpy(&val, &st->buf[i * sizeof(val)], sizeof(val));
		input_report_rel(si->idev, i, val);
	}

	if (si->code_n_mod) {
		val = 0;
		memcpy(&val, &st->buf[i * sizeof(val)], si->code_n_mod);
		input_report_rel(si->idev, i, val);
	}
	input_sync(si->idev);
	return 0;
}

static void nvs_input_remove(struct nvs_state *st)
{
	struct nvs_state_input *si = nvs_st_kif(st);

	if (si->idev) {
		if (si->input_no != -1 && (st->cfg->flags &
					   SENSOR_FLAG_DYNAMIC_SENSOR))
			nvs_dsm_input(si->input_no, false, st->snsr_type,
				      st->cfg->uuid);
		input_unregister_device(si->idev);
	}
}

static int nvs_input_init(struct nvs_state *st)
{
	struct nvs_state_input *si = nvs_st_kif(st);
	struct input_dev *idev;
	unsigned int i;
	unsigned int n;
	int ret;

	si->input_no = -1;
	idev = devm_input_allocate_device(st->dev);
	if (!idev) {
		dev_err(st->dev, "ERR: devm_input_allocate_device\n");
		return -ENOMEM;
	}

	idev->name = st->cfg->name;
	idev->id.version = st->cfg->version;
	idev->dev.parent = st->dev;
	/* input can only handle 32-bit data so we'll break up all the channel
	 * data, regardless of size, into 32-bit chunks.
	 */
	/* get total number of bytes we'll be sending per event */
	n = 0;
	for (i = 0; i < st->ch_n; i++)
		n += st->buf_ch[i].byte_n;
	si->code_n_mod = n % 4;
	n /= 4;
	si->code_n = n;
	if (si->code_n_mod)
		n++;
	for (i = 0; i < n; i++)
		input_set_capability(idev, EV_REL, i);

	ret = input_register_device(idev);
	if (ret) {
		dev_err(st->dev, "%s input_register_device ERR=%d\n",
			__func__, ret);
		return ret;
	}

	si->idev = idev;
	dev_set_drvdata(&idev->dev, st);
	st->attr_grp.name = "attr";
	ret = sysfs_create_group(&idev->dev.kobj,
				(const struct attribute_group *)&st->attr_grp);
	if (ret) {
		st->attr_grp.attrs = NULL;
		nvs_input_remove(st);
		return ret;
	}

	if (st->cfg->flags & SENSOR_FLAG_DYNAMIC_SENSOR) {
		sscanf(idev->dev.kobj.name, "input%lu", &si->input_no);
		nvs_dsm_input(si->input_no, true, st->snsr_type, st->cfg->uuid);
	}
	return 0;
}

static struct nvs_kif_fn nvs_kif_fn_input = {
	.name				= NVS_INPUT_DRIVER_NAME,
	.driver_version			= NVS_INPUT_DRIVER_VERSION,
	.init				= nvs_input_init,
	.remove				= nvs_input_remove,
	.push				= nvs_input_push,
};

static int nvs_probe_input(void **handle, void *dev_client, struct device *dev,
			   struct nvs_fn_dev *fn_dev,
			   struct sensor_cfg *snsr_cfg)
{
	return nvs_probe(handle, dev_client, dev, fn_dev, snsr_cfg,
			 &nvs_kif_fn_input, sizeof(struct nvs_state_input));
}

static struct nvs_fn_if nvs_fn_if_input = {
	.probe				= nvs_probe_input,
	.remove				= nvs_remove,
	.shutdown			= nvs_shutdown,
	.nvs_mutex_lock			= nvs_mutex_lock,
	.nvs_mutex_unlock		= nvs_mutex_unlock,
	.suspend			= nvs_suspend,
	.resume				= nvs_resume,
	.handler			= nvs_handler,
};

struct nvs_fn_if *nvs_input(void)
{
	return &nvs_fn_if_input;
}
EXPORT_SYMBOL_GPL(nvs_input);

MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("NVidia Sensor Input module");
MODULE_AUTHOR("NVIDIA Corporation");