/* * Greybus Vibrator protocol driver. * * Copyright 2014 Google Inc. * Copyright 2014 Linaro Ltd. * * Released under the GPLv2 only. */ #include #include #include #include #include #include #include "greybus.h" struct gb_vibrator_device { struct gb_connection *connection; struct device *dev; int minor; /* vibrator minor number */ u8 version_major; u8 version_minor; }; /* Version of the Greybus vibrator protocol we support */ #define GB_VIBRATOR_VERSION_MAJOR 0x00 #define GB_VIBRATOR_VERSION_MINOR 0x01 /* Greybus Vibrator request types */ #define GB_VIBRATOR_TYPE_INVALID 0x00 #define GB_VIBRATOR_TYPE_PROTOCOL_VERSION 0x01 #define GB_VIBRATOR_TYPE_ON 0x02 #define GB_VIBRATOR_TYPE_OFF 0x03 #define GB_VIBRATOR_TYPE_RESPONSE 0x80 /* OR'd with rest */ struct gb_vibrator_on_request { __le16 timeout_ms; }; /* Define get_version() routine */ define_get_version(gb_vibrator_device, VIBRATOR); static int turn_on(struct gb_vibrator_device *vib, u16 timeout_ms) { struct gb_vibrator_on_request request; request.timeout_ms = cpu_to_le16(timeout_ms); return gb_operation_sync(vib->connection, GB_VIBRATOR_TYPE_ON, &request, sizeof(request), NULL, 0); } static int turn_off(struct gb_vibrator_device *vib) { return gb_operation_sync(vib->connection, GB_VIBRATOR_TYPE_OFF, NULL, 0, NULL, 0); } static ssize_t timeout_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct gb_vibrator_device *vib = dev_get_drvdata(dev); unsigned long val; int retval; retval = kstrtoul(buf, 10, &val); if (retval < 0) { dev_err(dev, "could not parse timeout value %d\n", retval); return retval; } if (val) retval = turn_on(vib, (u16)val); else retval = turn_off(vib); if (retval) return retval; return count; } static DEVICE_ATTR_WO(timeout); static struct attribute *vibrator_attrs[] = { &dev_attr_timeout.attr, NULL, }; ATTRIBUTE_GROUPS(vibrator); static struct class vibrator_class = { .name = "vibrator", .owner = THIS_MODULE, #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0) .dev_groups = vibrator_groups, #endif }; static DEFINE_IDR(minors); static int gb_vibrator_connection_init(struct gb_connection *connection) { struct gb_vibrator_device *vib; struct device *dev; int retval; vib = kzalloc(sizeof(*vib), GFP_KERNEL); if (!vib) return -ENOMEM; vib->connection = connection; connection->private = vib; retval = get_version(vib); if (retval) goto error; /* * For now we create a device in sysfs for the vibrator, but odds are * there is a "real" device somewhere in the kernel for this, but I * can't find it at the moment... */ vib->minor = idr_alloc(&minors, vib, 0, 0, GFP_KERNEL); if (vib->minor < 0) { retval = vib->minor; goto error; } dev = device_create(&vibrator_class, &connection->dev, MKDEV(0, 0), vib, "vibrator%d", vib->minor); if (IS_ERR(dev)) { retval = -EINVAL; goto err_idr_remove; } vib->dev = dev; #if LINUX_VERSION_CODE <= KERNEL_VERSION(3,11,0) /* * Newer kernels handle this in a race-free manner, by the dev_groups * field in the struct class up above. But for older kernels, we need * to "open code this :( */ retval = sysfs_create_group(&dev->kobj, vibrator_groups[0]); if (retval) { device_unregister(dev); goto err_idr_remove; } #endif return 0; err_idr_remove: idr_remove(&minors, vib->minor); error: kfree(vib); return retval; } static void gb_vibrator_connection_exit(struct gb_connection *connection) { struct gb_vibrator_device *vib = connection->private; #if LINUX_VERSION_CODE <= KERNEL_VERSION(3,11,0) sysfs_remove_group(&vib->dev->kobj, vibrator_groups[0]); #endif idr_remove(&minors, vib->minor); device_unregister(vib->dev); kfree(vib); } static struct gb_protocol vibrator_protocol = { .name = "vibrator", .id = GREYBUS_PROTOCOL_VIBRATOR, .major = 0, .minor = 1, .connection_init = gb_vibrator_connection_init, .connection_exit = gb_vibrator_connection_exit, .request_recv = NULL, /* no incoming requests */ }; static __init int protocol_init(void) { int retval; retval = class_register(&vibrator_class); if (retval) return retval; return gb_protocol_register(&vibrator_protocol); } module_init(protocol_init); static __exit void protocol_exit(void) { gb_protocol_deregister(&vibrator_protocol); class_unregister(&vibrator_class); } module_exit(protocol_exit); MODULE_LICENSE("GPL v2");