summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorJun Yan <juyan@nvidia.com>2013-09-12 11:15:31 -0700
committerAjay Nandakumar <anandakumarm@nvidia.com>2013-10-03 19:17:43 +0530
commitaebcac70ac202028dcbbbe6ffe6a57e234e270f1 (patch)
tree5660920431d6f2a25fdcdc64fd5ab2375adbf128 /drivers
parentc49ce44c94bcc3f4ec68365f76e9b477e70a3ea2 (diff)
usb: misc: add LED driver for Nvidia Shield
Nvidia Shield use USB microcontroller to control LED. This driver use USB control message to change LED state. Bug 1344169 Change-Id: I81ae5ecb71396a02eae1ef6ebc8e6ba2263ed5c2 Signed-off-by: Jun Yan <juyan@nvidia.com> Reviewed-on: http://git-master/r/273851 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com> (cherry picked from commit 80e21dda9848f273752faa69c3b43a228cb394ee) Signed-off-by: Ajay Nandakumar <anandakumarm@nvidia.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/usb/misc/Kconfig7
-rw-r--r--drivers/usb/misc/Makefile1
-rw-r--r--drivers/usb/misc/usb_nvshieldled.c239
3 files changed, 247 insertions, 0 deletions
diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig
index 20bc14483f13..8eb9916dc4e6 100644
--- a/drivers/usb/misc/Kconfig
+++ b/drivers/usb/misc/Kconfig
@@ -246,3 +246,10 @@ config USB_RENESAS_MODEM
To compile this driver as a module, choose M here: the
module will be called renesas modem.
+
+config USB_NV_SHIELD_LED
+ tristate "USB Nvidia Shield LED driver support"
+ depends on USB
+ help
+ This driver communicates with the microcontroller on Nvidia Shield
+ to control the center button LED.
diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile
index 9c82f1086d07..608bc47f1e5c 100644
--- a/drivers/usb/misc/Makefile
+++ b/drivers/usb/misc/Makefile
@@ -30,3 +30,4 @@ obj-$(CONFIG_USB_HSIC_USB3503) += usb3503.o
obj-$(CONFIG_USB_RENESAS_MODEM) += boot_hsic_class.o
obj-$(CONFIG_USB_SISUSBVGA) += sisusbvga/
+obj-$(CONFIG_USB_NV_SHIELD_LED) += usb_nvshieldled.o
diff --git a/drivers/usb/misc/usb_nvshieldled.c b/drivers/usb/misc/usb_nvshieldled.c
new file mode 100644
index 000000000000..caed3a1a807f
--- /dev/null
+++ b/drivers/usb/misc/usb_nvshieldled.c
@@ -0,0 +1,239 @@
+/*
+ * USB LED Driver for NVIDIA Shield
+ *
+ * Copyright (c) 2013, NVIDIA Corporation. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/slab.h>
+
+#define DRIVER_AUTHOR "Jun Yan, juyan@nvidia.com"
+#define DRIVER_DESC "NVIDIA Shield USB LED Driver"
+
+#define INTF_CLASS 0xFF
+#define INTF_SUBCLASS 0x01
+#define INTF_PROTOCOL 0x01
+
+#define LED_STATE_NORMAL "normal"
+#define LED_STATE_BLINK "blink"
+#define LED_STATE_BREATHE "breathe"
+#define LED_STATE_OFF "off"
+#define LED_STATE_UNKNOW "unknow"
+
+#define LED_STATE_MAXCHAR 8
+
+#define UC_COMM_ON 0x10
+#define UC_COMM_BLINK 0x11
+#define UC_COMM_BREATHE 0x12
+#define UC_COMM_OFF 0x13
+
+#define VID 0x0955
+#define PID 0x7205
+
+static const struct usb_device_id nvshield_table[] = {
+ { USB_INTERFACE_INFO(INTF_CLASS, INTF_SUBCLASS, INTF_PROTOCOL) },
+ { USB_DEVICE(VID, PID)},
+ {}
+};
+MODULE_DEVICE_TABLE(usb, nvshield_table);
+
+enum led_state {
+ LED_NORMAL,
+ LED_BLINK,
+ LED_BREATHE,
+ LED_OFF,
+};
+
+struct nvshield_led {
+ struct usb_device *udev;
+ unsigned char brightness;
+ enum led_state state;
+};
+
+static void send_command(struct nvshield_led *led)
+{
+ int retval = 0;
+ unsigned char state;
+
+ if (led->state == LED_BREATHE)
+ state = UC_COMM_BREATHE;
+ else if (led->state == LED_BLINK)
+ state = UC_COMM_BLINK;
+ else if (led->state == LED_NORMAL)
+ state = UC_COMM_ON;
+ else if (led->state == LED_OFF)
+ state = UC_COMM_OFF;
+ else
+ return;
+
+ retval = usb_control_msg(led->udev,
+ usb_sndctrlpipe(led->udev, 0),
+ 0x00,
+ 0x42,
+ cpu_to_le16(led->brightness),
+ cpu_to_le16(state),
+ NULL,
+ 0,
+ 2000);
+}
+
+static ssize_t show_brightness(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct nvshield_led *led = usb_get_intfdata(intf);
+
+ return sprintf(buf, "%d\n", led->brightness);
+}
+
+static ssize_t set_brightness(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct nvshield_led *led = usb_get_intfdata(intf);
+ int brightness_val;
+
+ if (!kstrtoul(buf, 10, &brightness_val)) {
+ led->brightness = brightness_val;
+ send_command(led);
+ }
+ return count;
+}
+
+static ssize_t show_ledstate(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct nvshield_led *led = usb_get_intfdata(intf);
+
+ switch (led->state) {
+ case LED_NORMAL:
+ return sprintf(buf, "%s\n", LED_STATE_NORMAL);
+ case LED_BLINK:
+ return sprintf(buf, "%s\n", LED_STATE_BLINK);
+ case LED_BREATHE:
+ return sprintf(buf, "%s\n", LED_STATE_BREATHE);
+ case LED_OFF:
+ return sprintf(buf, "%s\n", LED_STATE_OFF);
+ default:
+ return sprintf(buf, LED_STATE_UNKNOW);
+ }
+}
+
+static ssize_t set_ledstate(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct nvshield_led *led = usb_get_intfdata(intf);
+ char ledstate_name[LED_STATE_MAXCHAR];
+ size_t len;
+
+ ledstate_name[sizeof(ledstate_name) - 1] = '\0';
+ strncpy(ledstate_name, buf, sizeof(ledstate_name) - 1);
+ len = strlen(ledstate_name);
+
+ if (len && ledstate_name[len - 1] == '\n')
+ ledstate_name[len - 1] = '\0';
+
+ if (!strcmp(ledstate_name, LED_STATE_NORMAL))
+ led->state = LED_NORMAL;
+ else if (!strcmp(ledstate_name, LED_STATE_BLINK))
+ led->state = LED_BLINK;
+ else if (!strcmp(ledstate_name, LED_STATE_BREATHE))
+ led->state = LED_BREATHE;
+ else if (!strcmp(ledstate_name, LED_STATE_OFF))
+ led->state = LED_OFF;
+ else
+ return count;
+
+ send_command(led);
+ return count;
+}
+
+static DEVICE_ATTR(brightness, S_IRUGO | S_IWUSR,
+ show_brightness, set_brightness);
+static DEVICE_ATTR(state, S_IRUGO | S_IWUSR,
+ show_ledstate, set_ledstate);
+
+static int nvshieldled_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(interface);
+ struct nvshield_led *dev = NULL;
+ int retval = -ENOMEM;
+
+ dev = kzalloc(sizeof(struct nvshield_led), GFP_KERNEL);
+ if (dev == NULL) {
+ dev_err(&interface->dev, "out of memory\n");
+ goto error_mem;
+ }
+
+ dev->udev = usb_get_dev(udev);
+ dev->state = LED_NORMAL;
+ dev->brightness = 255;
+
+ usb_set_intfdata(interface, dev);
+
+ retval = device_create_file(&interface->dev, &dev_attr_brightness);
+ if (retval)
+ goto error;
+ retval = device_create_file(&interface->dev, &dev_attr_state);
+ if (retval)
+ goto error;
+ dev_info(&interface->dev, "Nvidia Shield LED attached\n");
+ return 0;
+
+error:
+ device_remove_file(&interface->dev, &dev_attr_brightness);
+ device_remove_file(&interface->dev, &dev_attr_state);
+ usb_set_intfdata(interface, NULL);
+ usb_put_dev(dev->udev);
+ kfree(dev);
+error_mem:
+ return retval;
+}
+
+static void nvshieldled_disconnect(struct usb_interface *interface)
+{
+ struct nvshield_led *dev;
+
+ dev = usb_get_intfdata(interface);
+
+ device_remove_file(&interface->dev, &dev_attr_brightness);
+ device_remove_file(&interface->dev, &dev_attr_state);
+
+ usb_set_intfdata(interface, NULL);
+ usb_put_dev(dev->udev);
+ kfree(dev);
+ dev_info(&interface->dev, "Nvidia Shield LED disconnected\n");
+}
+
+static struct usb_driver shieldled_driver = {
+ .name = "nvshieldled",
+ .probe = nvshieldled_probe,
+ .disconnect = nvshieldled_disconnect,
+ .id_table = nvshield_table,
+};
+
+module_usb_driver(shieldled_driver);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");