blob: 5afcb2784a32643a8823dde0c50246e67060aee1 [file] [log] [blame]
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -08001/*
Greg Kroah-Hartman4efe6062014-11-17 16:55:54 -08002 * Greybus Vibrator protocol driver.
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -08003 *
4 * Copyright 2014 Google Inc.
Alex Eldera46e9672014-12-12 12:08:42 -06005 * Copyright 2014 Linaro Ltd.
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -08006 *
7 * Released under the GPLv2 only.
8 */
9
10#include <linux/kernel.h>
11#include <linux/module.h>
12#include <linux/slab.h>
13#include <linux/device.h>
14#include <linux/kdev_t.h>
Greg Kroah-Hartman396671b2014-11-17 17:12:50 -080015#include <linux/idr.h>
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -080016#include "greybus.h"
17
18struct gb_vibrator_device {
19 struct gb_connection *connection;
20 struct device *dev;
Greg Kroah-Hartman396671b2014-11-17 17:12:50 -080021 int minor; /* vibrator minor number */
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -080022};
23
Greg Kroah-Hartman42a94662014-11-19 10:36:23 -080024/* Version of the Greybus vibrator protocol we support */
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -080025#define GB_VIBRATOR_VERSION_MAJOR 0x00
26#define GB_VIBRATOR_VERSION_MINOR 0x01
27
Alex Elder6d653372015-05-07 13:03:52 -050028/* Greybus Vibrator operation types */
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -080029#define GB_VIBRATOR_TYPE_ON 0x02
30#define GB_VIBRATOR_TYPE_OFF 0x03
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -080031
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -080032struct gb_vibrator_on_request {
33 __le16 timeout_ms;
34};
35
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -080036static int turn_on(struct gb_vibrator_device *vib, u16 timeout_ms)
37{
Greg Kroah-Hartmanac3d2492014-11-23 17:45:20 -080038 struct gb_vibrator_on_request request;
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -080039
Greg Kroah-Hartmanac3d2492014-11-23 17:45:20 -080040 request.timeout_ms = cpu_to_le16(timeout_ms);
41 return gb_operation_sync(vib->connection, GB_VIBRATOR_TYPE_ON,
42 &request, sizeof(request), NULL, 0);
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -080043}
44
45static int turn_off(struct gb_vibrator_device *vib)
46{
Greg Kroah-Hartmanac3d2492014-11-23 17:45:20 -080047 return gb_operation_sync(vib->connection, GB_VIBRATOR_TYPE_OFF,
48 NULL, 0, NULL, 0);
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -080049}
50
51static ssize_t timeout_store(struct device *dev, struct device_attribute *attr,
52 const char *buf, size_t count)
53{
54 struct gb_vibrator_device *vib = dev_get_drvdata(dev);
55 unsigned long val;
56 int retval;
57
58 retval = kstrtoul(buf, 10, &val);
59 if (retval < 0) {
60 dev_err(dev, "could not parse timeout value %d\n", retval);
61 return retval;
62 }
63
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -080064 if (val)
65 retval = turn_on(vib, (u16)val);
66 else
67 retval = turn_off(vib);
68 if (retval)
69 return retval;
70
71 return count;
72}
73static DEVICE_ATTR_WO(timeout);
74
75static struct attribute *vibrator_attrs[] = {
76 &dev_attr_timeout.attr,
77 NULL,
78};
79ATTRIBUTE_GROUPS(vibrator);
80
81static struct class vibrator_class = {
82 .name = "vibrator",
83 .owner = THIS_MODULE,
84#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)
85 .dev_groups = vibrator_groups,
86#endif
87};
88
Greg Kroah-Hartman6b174922015-05-14 10:39:35 -070089static DEFINE_IDA(minors);
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -080090
Johan Hovolde0deb072016-01-21 17:34:24 +010091static int gb_vibrator_probe(struct gb_bundle *bundle,
92 const struct greybus_bundle_id *id)
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -080093{
Johan Hovolde0deb072016-01-21 17:34:24 +010094 struct greybus_descriptor_cport *cport_desc;
95 struct gb_connection *connection;
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -080096 struct gb_vibrator_device *vib;
97 struct device *dev;
98 int retval;
99
Johan Hovolde0deb072016-01-21 17:34:24 +0100100 if (bundle->num_cports != 1)
101 return -ENODEV;
102
103 cport_desc = &bundle->cport_desc[0];
104 if (cport_desc->protocol_id != GREYBUS_PROTOCOL_VIBRATOR)
105 return -ENODEV;
106
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800107 vib = kzalloc(sizeof(*vib), GFP_KERNEL);
108 if (!vib)
109 return -ENOMEM;
110
Johan Hovolde0deb072016-01-21 17:34:24 +0100111 connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id),
112 NULL);
113 if (IS_ERR(connection)) {
114 retval = PTR_ERR(connection);
115 goto err_free_vib;
116 }
Greg Kroah-Hartman0ec30632016-03-22 14:30:35 -0400117 gb_connection_set_data(connection, vib);
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800118
Johan Hovolde0deb072016-01-21 17:34:24 +0100119 vib->connection = connection;
120
121 greybus_set_drvdata(bundle, vib);
122
123 retval = gb_connection_enable(connection);
124 if (retval)
125 goto err_connection_destroy;
126
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800127 /*
Greg Kroah-Hartman396671b2014-11-17 17:12:50 -0800128 * For now we create a device in sysfs for the vibrator, but odds are
129 * there is a "real" device somewhere in the kernel for this, but I
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800130 * can't find it at the moment...
131 */
Greg Kroah-Hartman6b174922015-05-14 10:39:35 -0700132 vib->minor = ida_simple_get(&minors, 0, 0, GFP_KERNEL);
Greg Kroah-Hartman396671b2014-11-17 17:12:50 -0800133 if (vib->minor < 0) {
134 retval = vib->minor;
Johan Hovolde0deb072016-01-21 17:34:24 +0100135 goto err_connection_disable;
Greg Kroah-Hartman396671b2014-11-17 17:12:50 -0800136 }
Johan Hovolde0deb072016-01-21 17:34:24 +0100137 dev = device_create(&vibrator_class, &bundle->dev,
Greg Kroah-Hartman5ae2f552015-10-14 11:12:06 -0700138 MKDEV(0, 0), vib, "vibrator%d", vib->minor);
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800139 if (IS_ERR(dev)) {
140 retval = -EINVAL;
Greg Kroah-Hartman6b174922015-05-14 10:39:35 -0700141 goto err_ida_remove;
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800142 }
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800143 vib->dev = dev;
144
145#if LINUX_VERSION_CODE <= KERNEL_VERSION(3,11,0)
146 /*
Greg Kroah-Hartman4efe6062014-11-17 16:55:54 -0800147 * Newer kernels handle this in a race-free manner, by the dev_groups
148 * field in the struct class up above. But for older kernels, we need
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800149 * to "open code this :(
150 */
151 retval = sysfs_create_group(&dev->kobj, vibrator_groups[0]);
152 if (retval) {
153 device_unregister(dev);
Greg Kroah-Hartman6b174922015-05-14 10:39:35 -0700154 goto err_ida_remove;
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800155 }
156#endif
157
158 return 0;
159
Greg Kroah-Hartman6b174922015-05-14 10:39:35 -0700160err_ida_remove:
161 ida_simple_remove(&minors, vib->minor);
Johan Hovolde0deb072016-01-21 17:34:24 +0100162err_connection_disable:
163 gb_connection_disable(connection);
164err_connection_destroy:
165 gb_connection_destroy(connection);
166err_free_vib:
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800167 kfree(vib);
Johan Hovolde0deb072016-01-21 17:34:24 +0100168
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800169 return retval;
170}
171
Johan Hovolde0deb072016-01-21 17:34:24 +0100172static void gb_vibrator_disconnect(struct gb_bundle *bundle)
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800173{
Johan Hovolde0deb072016-01-21 17:34:24 +0100174 struct gb_vibrator_device *vib = greybus_get_drvdata(bundle);
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800175
176#if LINUX_VERSION_CODE <= KERNEL_VERSION(3,11,0)
177 sysfs_remove_group(&vib->dev->kobj, vibrator_groups[0]);
178#endif
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800179 device_unregister(vib->dev);
Johan Hovoldd7849bf2015-09-14 20:19:02 +0200180 ida_simple_remove(&minors, vib->minor);
Johan Hovolde0deb072016-01-21 17:34:24 +0100181 gb_connection_disable(vib->connection);
182 gb_connection_destroy(vib->connection);
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800183 kfree(vib);
184}
185
Johan Hovolde0deb072016-01-21 17:34:24 +0100186static const struct greybus_bundle_id gb_vibrator_id_table[] = {
187 { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_VIBRATOR) },
188 { }
189};
190MODULE_DEVICE_TABLE(greybus, gb_vibrator_id_table);
191
192static struct greybus_driver gb_vibrator_driver = {
193 .name = "vibrator",
194 .probe = gb_vibrator_probe,
195 .disconnect = gb_vibrator_disconnect,
196 .id_table = gb_vibrator_id_table,
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800197};
198
Johan Hovolde0deb072016-01-21 17:34:24 +0100199static __init int gb_vibrator_init(void)
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800200{
201 int retval;
202
203 retval = class_register(&vibrator_class);
204 if (retval)
205 return retval;
206
Johan Hovolde0deb072016-01-21 17:34:24 +0100207 retval = greybus_register(&gb_vibrator_driver);
Johan Hovoldd4efa682015-10-13 19:10:27 +0200208 if (retval)
209 goto err_class_unregister;
210
211 return 0;
212
213err_class_unregister:
214 class_unregister(&vibrator_class);
215
216 return retval;
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800217}
Johan Hovolde0deb072016-01-21 17:34:24 +0100218module_init(gb_vibrator_init);
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800219
Johan Hovolde0deb072016-01-21 17:34:24 +0100220static __exit void gb_vibrator_exit(void)
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800221{
Johan Hovolde0deb072016-01-21 17:34:24 +0100222 greybus_deregister(&gb_vibrator_driver);
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800223 class_unregister(&vibrator_class);
Greg Kroah-Hartman5c1ac692015-07-08 10:44:09 -0700224 ida_destroy(&minors);
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800225}
Johan Hovolde0deb072016-01-21 17:34:24 +0100226module_exit(gb_vibrator_exit);
Greg Kroah-Hartman66b676f2014-12-24 13:01:41 -0800227
228MODULE_LICENSE("GPL v2");