Greg Kroah-Hartman | ac4029f | 2014-11-17 16:03:34 -0800 | [diff] [blame] | 1 | /* |
Greg Kroah-Hartman | 4efe606 | 2014-11-17 16:55:54 -0800 | [diff] [blame^] | 2 | * Greybus Vibrator protocol driver. |
Greg Kroah-Hartman | ac4029f | 2014-11-17 16:03:34 -0800 | [diff] [blame] | 3 | * |
| 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 | |
| 16 | struct 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 | |
| 34 | struct gb_vibrator_proto_version_response { |
| 35 | __u8 status; |
| 36 | __u8 major; |
| 37 | __u8 minor; |
| 38 | }; |
| 39 | |
| 40 | struct gb_vibrator_on_request { |
| 41 | __le16 timeout_ms; |
| 42 | }; |
| 43 | |
| 44 | struct gb_vibrator_simple_response { |
| 45 | __u8 status; |
| 46 | }; |
| 47 | |
| 48 | static 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 | } |
| 88 | out: |
| 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 | */ |
| 99 | static 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 | |
| 123 | static 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 | } |
| 152 | out: |
| 153 | gb_operation_destroy(operation); |
| 154 | |
| 155 | return retval; |
Greg Kroah-Hartman | ac4029f | 2014-11-17 16:03:34 -0800 | [diff] [blame] | 156 | } |
| 157 | |
| 158 | static int turn_off(struct gb_vibrator_device *vib) |
| 159 | { |
| 160 | struct gb_connection *connection = vib->connection; |
Greg Kroah-Hartman | ac4029f | 2014-11-17 16:03:34 -0800 | [diff] [blame] | 161 | |
Greg Kroah-Hartman | 4efe606 | 2014-11-17 16:55:54 -0800 | [diff] [blame^] | 162 | return request_operation(connection, GB_VIBRATOR_TYPE_OFF, NULL, 0); |
Greg Kroah-Hartman | ac4029f | 2014-11-17 16:03:34 -0800 | [diff] [blame] | 163 | } |
| 164 | |
| 165 | static 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 | } |
| 189 | static DEVICE_ATTR_WO(timeout); |
| 190 | |
| 191 | static struct attribute *vibrator_attrs[] = { |
| 192 | &dev_attr_timeout.attr, |
| 193 | NULL, |
| 194 | }; |
| 195 | ATTRIBUTE_GROUPS(vibrator); |
| 196 | |
| 197 | static 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 | |
| 205 | static int minor; |
| 206 | |
| 207 | static 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-Hartman | 4efe606 | 2014-11-17 16:55:54 -0800 | [diff] [blame^] | 228 | dev = device_create(&vibrator_class, &connection->dev, MKDEV(0, 0), vib, |
Greg Kroah-Hartman | ac4029f | 2014-11-17 16:03:34 -0800 | [diff] [blame] | 229 | "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-Hartman | 4efe606 | 2014-11-17 16:55:54 -0800 | [diff] [blame^] | 239 | * 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-Hartman | ac4029f | 2014-11-17 16:03:34 -0800 | [diff] [blame] | 241 | * 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 | |
| 252 | error: |
| 253 | kfree(vib); |
| 254 | return retval; |
| 255 | } |
| 256 | |
| 257 | static 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 | |
| 268 | static 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 | |
| 277 | bool 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 | |
| 288 | void gb_vibrator_protocol_exit(void) |
| 289 | { |
| 290 | gb_protocol_deregister(&vibrator_protocol); |
| 291 | class_unregister(&vibrator_class); |
| 292 | } |