blob: 22356afb1bad931b221bec976f877e5a3d77a1a6 [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.
5 *
6 * Released under the GPLv2 only.
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 "greybus.h"
15
16struct gb_vibrator_device {
17 struct gb_connection *connection;
18 struct device *dev;
19 u8 version_major;
20 u8 version_minor;
21};
22
23/* Version of the Greybus i2c protocol we support */
24#define GB_VIBRATOR_VERSION_MAJOR 0x00
25#define GB_VIBRATOR_VERSION_MINOR 0x01
26
27/* Greybus Vibrator request types */
28#define GB_VIBRATOR_TYPE_INVALID 0x00
29#define GB_VIBRATOR_TYPE_PROTOCOL_VERSION 0x01
30#define GB_VIBRATOR_TYPE_ON 0x02
31#define GB_VIBRATOR_TYPE_OFF 0x03
32#define GB_VIBRATOR_TYPE_RESPONSE 0x80 /* OR'd with rest */
33
34struct gb_vibrator_proto_version_response {
35 __u8 status;
36 __u8 major;
37 __u8 minor;
38};
39
40struct gb_vibrator_on_request {
41 __le16 timeout_ms;
42};
43
44struct gb_vibrator_simple_response {
45 __u8 status;
46};
47
48static int request_operation(struct gb_connection *connection, int type,
49 void *response, int response_size)
50{
51 struct gb_operation *operation;
52 struct gb_vibrator_simple_response *fake_request;
53 u8 *local_response;
54 int ret;
55
56 local_response = kmalloc(response_size, GFP_KERNEL);
57 if (!local_response)
58 return -ENOMEM;
59
60 operation = gb_operation_create(connection, type, 0, response_size);
61 if (!operation) {
62 kfree(local_response);
63 return -ENOMEM;
64 }
65
66 /* Synchronous operation--no callback */
67 ret = gb_operation_request_send(operation, NULL);
68 if (ret) {
69 pr_err("version operation failed (%d)\n", ret);
70 goto out;
71 }
72
73 /*
74 * We only want to look at the status, and all requests have the same
75 * layout for where the status is, so cast this to a random request so
76 * we can see the status easier.
77 */
78 fake_request = (struct gb_vibrator_simple_response *)local_response;
79 if (fake_request->status) {
80 gb_connection_err(connection, "response %hhu",
81 fake_request->status);
82 ret = -EIO;
83 } else {
84 /* Good request, so copy to the caller's buffer */
85 if (response_size && response)
86 memcpy(response, local_response, response_size);
87 }
88out:
89 gb_operation_destroy(operation);
90 kfree(local_response);
91
92 return ret;
93}
94
95/*
96 * This request only uses the connection field, and if successful,
97 * fills in the major and minor protocol version of the target.
98 */
99static int get_version(struct gb_vibrator_device *vib)
100{
101 struct gb_connection *connection = vib->connection;
102 struct gb_vibrator_proto_version_response version_request;
103 int retval;
104
105 retval = request_operation(connection,
106 GB_VIBRATOR_TYPE_PROTOCOL_VERSION,
107 &version_request, sizeof(version_request));
108 if (retval)
109 return retval;
110
111 if (version_request.major > GB_VIBRATOR_VERSION_MAJOR) {
112 dev_err(&connection->dev,
113 "unsupported major version (%hhu > %hhu)\n",
114 version_request.major, GB_VIBRATOR_VERSION_MAJOR);
115 return -ENOTSUPP;
116 }
117
118 vib->version_major = version_request.major;
119 vib->version_minor = version_request.minor;
120 return 0;
121}
122
123static int turn_on(struct gb_vibrator_device *vib, u16 timeout_ms)
124{
125 struct gb_connection *connection = vib->connection;
126 struct gb_operation *operation;
127 struct gb_vibrator_on_request *request;
128 struct gb_vibrator_simple_response *response;
129 int retval;
130
131 operation = gb_operation_create(connection, GB_VIBRATOR_TYPE_ON,
132 sizeof(*request), sizeof(*response));
133 if (!operation)
134 return -ENOMEM;
135 request = operation->request_payload;
136 request->timeout_ms = cpu_to_le16(timeout_ms);
137
138 /* Synchronous operation--no callback */
139 retval = gb_operation_request_send(operation, NULL);
140 if (retval) {
141 dev_err(&connection->dev,
142 "send data operation failed (%d)\n", retval);
143 goto out;
144 }
145
146 response = operation->response_payload;
147 if (response->status) {
148 gb_connection_err(connection, "send data response %hhu",
149 response->status);
150 retval = -EIO;
151 }
152out:
153 gb_operation_destroy(operation);
154
155 return retval;
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800156}
157
158static int turn_off(struct gb_vibrator_device *vib)
159{
160 struct gb_connection *connection = vib->connection;
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800161
Greg Kroah-Hartman4efe6062014-11-17 16:55:54 -0800162 return request_operation(connection, GB_VIBRATOR_TYPE_OFF, NULL, 0);
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800163}
164
165static ssize_t timeout_store(struct device *dev, struct device_attribute *attr,
166 const char *buf, size_t count)
167{
168 struct gb_vibrator_device *vib = dev_get_drvdata(dev);
169 unsigned long val;
170 int retval;
171
172 retval = kstrtoul(buf, 10, &val);
173 if (retval < 0) {
174 dev_err(dev, "could not parse timeout value %d\n", retval);
175 return retval;
176 }
177
178 if (val < 0)
179 return -EINVAL;
180 if (val)
181 retval = turn_on(vib, (u16)val);
182 else
183 retval = turn_off(vib);
184 if (retval)
185 return retval;
186
187 return count;
188}
189static DEVICE_ATTR_WO(timeout);
190
191static struct attribute *vibrator_attrs[] = {
192 &dev_attr_timeout.attr,
193 NULL,
194};
195ATTRIBUTE_GROUPS(vibrator);
196
197static struct class vibrator_class = {
198 .name = "vibrator",
199 .owner = THIS_MODULE,
200#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)
201 .dev_groups = vibrator_groups,
202#endif
203};
204
205static int minor;
206
207static int gb_vibrator_connection_init(struct gb_connection *connection)
208{
209 struct gb_vibrator_device *vib;
210 struct device *dev;
211 int retval;
212
213 vib = kzalloc(sizeof(*vib), GFP_KERNEL);
214 if (!vib)
215 return -ENOMEM;
216
217 vib->connection = connection;
218
219 retval = get_version(vib);
220 if (retval)
221 goto error;
222
223 /*
224 * FIXME: for now we create a device in sysfs for the vibrator, but odds
225 * are there is a "real" device somewhere in the kernel for this, but I
226 * can't find it at the moment...
227 */
Greg Kroah-Hartman4efe6062014-11-17 16:55:54 -0800228 dev = device_create(&vibrator_class, &connection->dev, MKDEV(0, 0), vib,
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800229 "vibrator%d", minor);
230 if (IS_ERR(dev)) {
231 retval = -EINVAL;
232 goto error;
233 }
234 minor++;
235 vib->dev = dev;
236
237#if LINUX_VERSION_CODE <= KERNEL_VERSION(3,11,0)
238 /*
Greg Kroah-Hartman4efe6062014-11-17 16:55:54 -0800239 * Newer kernels handle this in a race-free manner, by the dev_groups
240 * field in the struct class up above. But for older kernels, we need
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800241 * to "open code this :(
242 */
243 retval = sysfs_create_group(&dev->kobj, vibrator_groups[0]);
244 if (retval) {
245 device_unregister(dev);
246 goto error;
247 }
248#endif
249
250 return 0;
251
252error:
253 kfree(vib);
254 return retval;
255}
256
257static void gb_vibrator_connection_exit(struct gb_connection *connection)
258{
259 struct gb_vibrator_device *vib = connection->private;
260
261#if LINUX_VERSION_CODE <= KERNEL_VERSION(3,11,0)
262 sysfs_remove_group(&vib->dev->kobj, vibrator_groups[0]);
263#endif
264 device_unregister(vib->dev);
265 kfree(vib);
266}
267
268static struct gb_protocol vibrator_protocol = {
269 .id = GREYBUS_PROTOCOL_VIBRATOR,
270 .major = 0,
271 .minor = 1,
272 .connection_init = gb_vibrator_connection_init,
273 .connection_exit = gb_vibrator_connection_exit,
274 .request_recv = NULL, /* no incoming requests */
275};
276
277bool gb_vibrator_protocol_init(void)
278{
279 int retval;
280
281 retval = class_register(&vibrator_class);
282 if (retval)
283 return retval;
284
285 return gb_protocol_register(&vibrator_protocol);
286}
287
288void gb_vibrator_protocol_exit(void)
289{
290 gb_protocol_deregister(&vibrator_protocol);
291 class_unregister(&vibrator_class);
292}