File Index
Files and directories
Sign in
FILE

vibrator.c

File preview with syntax highlighting.

linux-6.19.14/drivers/staging/greybus/vibrator.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Greybus Vibrator protocol driver.
  4 *
  5 * Copyright 2014 Google Inc.
  6 * Copyright 2014 Linaro Ltd.
  7 */
  8
  9#include <linux/kernel.h>
 10#include <linux/module.h>
 11#include <linux/slab.h>
 12#include <linux/device.h>
 13#include <linux/kdev_t.h>
 14#include <linux/idr.h>
 15#include <linux/pm_runtime.h>
 16#include <linux/greybus.h>
 17
 18struct gb_vibrator_device {
 19	struct gb_connection	*connection;
 20	struct device		*dev;
 21	int			minor;		/* vibrator minor number */
 22	struct delayed_work     delayed_work;
 23};
 24
 25/* Greybus Vibrator operation types */
 26#define	GB_VIBRATOR_TYPE_ON			0x02
 27#define	GB_VIBRATOR_TYPE_OFF			0x03
 28
 29static int turn_off(struct gb_vibrator_device *vib)
 30{
 31	struct gb_bundle *bundle = vib->connection->bundle;
 32	int ret;
 33
 34	ret = gb_operation_sync(vib->connection, GB_VIBRATOR_TYPE_OFF,
 35				NULL, 0, NULL, 0);
 36
 37	gb_pm_runtime_put_autosuspend(bundle);
 38
 39	return ret;
 40}
 41
 42static int turn_on(struct gb_vibrator_device *vib, u16 timeout_ms)
 43{
 44	struct gb_bundle *bundle = vib->connection->bundle;
 45	int ret;
 46
 47	ret = gb_pm_runtime_get_sync(bundle);
 48	if (ret)
 49		return ret;
 50
 51	/* Vibrator was switched ON earlier */
 52	if (cancel_delayed_work_sync(&vib->delayed_work))
 53		turn_off(vib);
 54
 55	ret = gb_operation_sync(vib->connection, GB_VIBRATOR_TYPE_ON,
 56				NULL, 0, NULL, 0);
 57	if (ret) {
 58		gb_pm_runtime_put_autosuspend(bundle);
 59		return ret;
 60	}
 61
 62	schedule_delayed_work(&vib->delayed_work, msecs_to_jiffies(timeout_ms));
 63
 64	return 0;
 65}
 66
 67static void gb_vibrator_worker(struct work_struct *work)
 68{
 69	struct delayed_work *delayed_work = to_delayed_work(work);
 70	struct gb_vibrator_device *vib =
 71		container_of(delayed_work,
 72			     struct gb_vibrator_device,
 73			     delayed_work);
 74
 75	turn_off(vib);
 76}
 77
 78static ssize_t timeout_store(struct device *dev, struct device_attribute *attr,
 79			     const char *buf, size_t count)
 80{
 81	struct gb_vibrator_device *vib = dev_get_drvdata(dev);
 82	unsigned long val;
 83	int retval;
 84
 85	retval = kstrtoul(buf, 10, &val);
 86	if (retval < 0) {
 87		dev_err(dev, "could not parse timeout value %d\n", retval);
 88		return retval;
 89	}
 90
 91	if (val)
 92		retval = turn_on(vib, (u16)val);
 93	else
 94		retval = turn_off(vib);
 95	if (retval)
 96		return retval;
 97
 98	return count;
 99}
100static DEVICE_ATTR_WO(timeout);
101
102static struct attribute *vibrator_attrs[] = {
103	&dev_attr_timeout.attr,
104	NULL,
105};
106ATTRIBUTE_GROUPS(vibrator);
107
108static struct class vibrator_class = {
109	.name		= "vibrator",
110	.dev_groups	= vibrator_groups,
111};
112
113static DEFINE_IDA(minors);
114
115static int gb_vibrator_probe(struct gb_bundle *bundle,
116			     const struct greybus_bundle_id *id)
117{
118	struct greybus_descriptor_cport *cport_desc;
119	struct gb_connection *connection;
120	struct gb_vibrator_device *vib;
121	struct device *dev;
122	int retval;
123
124	if (bundle->num_cports != 1)
125		return -ENODEV;
126
127	cport_desc = &bundle->cport_desc[0];
128	if (cport_desc->protocol_id != GREYBUS_PROTOCOL_VIBRATOR)
129		return -ENODEV;
130
131	vib = kzalloc(sizeof(*vib), GFP_KERNEL);
132	if (!vib)
133		return -ENOMEM;
134
135	connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id),
136					  NULL);
137	if (IS_ERR(connection)) {
138		retval = PTR_ERR(connection);
139		goto err_free_vib;
140	}
141	gb_connection_set_data(connection, vib);
142
143	vib->connection = connection;
144
145	greybus_set_drvdata(bundle, vib);
146
147	retval = gb_connection_enable(connection);
148	if (retval)
149		goto err_connection_destroy;
150
151	/*
152	 * For now we create a device in sysfs for the vibrator, but odds are
153	 * there is a "real" device somewhere in the kernel for this, but I
154	 * can't find it at the moment...
155	 */
156	vib->minor = ida_alloc(&minors, GFP_KERNEL);
157	if (vib->minor < 0) {
158		retval = vib->minor;
159		goto err_connection_disable;
160	}
161	dev = device_create(&vibrator_class, &bundle->dev,
162			    MKDEV(0, 0), vib, "vibrator%d", vib->minor);
163	if (IS_ERR(dev)) {
164		retval = -EINVAL;
165		goto err_ida_remove;
166	}
167	vib->dev = dev;
168
169	INIT_DELAYED_WORK(&vib->delayed_work, gb_vibrator_worker);
170
171	gb_pm_runtime_put_autosuspend(bundle);
172
173	return 0;
174
175err_ida_remove:
176	ida_free(&minors, vib->minor);
177err_connection_disable:
178	gb_connection_disable(connection);
179err_connection_destroy:
180	gb_connection_destroy(connection);
181err_free_vib:
182	kfree(vib);
183
184	return retval;
185}
186
187static void gb_vibrator_disconnect(struct gb_bundle *bundle)
188{
189	struct gb_vibrator_device *vib = greybus_get_drvdata(bundle);
190	int ret;
191
192	ret = gb_pm_runtime_get_sync(bundle);
193	if (ret)
194		gb_pm_runtime_get_noresume(bundle);
195
196	if (cancel_delayed_work_sync(&vib->delayed_work))
197		turn_off(vib);
198
199	device_unregister(vib->dev);
200	ida_free(&minors, vib->minor);
201	gb_connection_disable(vib->connection);
202	gb_connection_destroy(vib->connection);
203	kfree(vib);
204}
205
206static const struct greybus_bundle_id gb_vibrator_id_table[] = {
207	{ GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_VIBRATOR) },
208	{ }
209};
210MODULE_DEVICE_TABLE(greybus, gb_vibrator_id_table);
211
212static struct greybus_driver gb_vibrator_driver = {
213	.name		= "vibrator",
214	.probe		= gb_vibrator_probe,
215	.disconnect	= gb_vibrator_disconnect,
216	.id_table	= gb_vibrator_id_table,
217};
218
219static __init int gb_vibrator_init(void)
220{
221	int retval;
222
223	retval = class_register(&vibrator_class);
224	if (retval)
225		return retval;
226
227	retval = greybus_register(&gb_vibrator_driver);
228	if (retval)
229		goto err_class_unregister;
230
231	return 0;
232
233err_class_unregister:
234	class_unregister(&vibrator_class);
235
236	return retval;
237}
238module_init(gb_vibrator_init);
239
240static __exit void gb_vibrator_exit(void)
241{
242	greybus_deregister(&gb_vibrator_driver);
243	class_unregister(&vibrator_class);
244	ida_destroy(&minors);
245}
246module_exit(gb_vibrator_exit);
247
248MODULE_DESCRIPTION("Greybus Vibrator protocol driver");
249MODULE_LICENSE("GPL v2");
~/ Read-only file preview.