diff options
author | Jun Yan <juyan@nvidia.com> | 2013-09-12 11:15:31 -0700 |
---|---|---|
committer | Ajay Nandakumar <anandakumarm@nvidia.com> | 2013-10-03 19:17:43 +0530 |
commit | aebcac70ac202028dcbbbe6ffe6a57e234e270f1 (patch) | |
tree | 5660920431d6f2a25fdcdc64fd5ab2375adbf128 /drivers | |
parent | c49ce44c94bcc3f4ec68365f76e9b477e70a3ea2 (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/Kconfig | 7 | ||||
-rw-r--r-- | drivers/usb/misc/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/misc/usb_nvshieldled.c | 239 |
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"); |