linux/drivers/hid/hid-gt683r.c
<<
>>
Prefs
   1/*
   2 * MSI GT683R led driver
   3 *
   4 * Copyright (c) 2014 Janne Kanniainen <janne.kanniainen@gmail.com>
   5 *
   6 * This program is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU General Public License as
   8 * published by the Free Software Foundation; either version 2 of
   9 * the License, or (at your option) any later version.
  10 *
  11 * This program is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 * GNU General Public License for more details.
  15 *
  16 */
  17
  18#include <linux/device.h>
  19#include <linux/hid.h>
  20#include <linux/kernel.h>
  21#include <linux/leds.h>
  22#include <linux/module.h>
  23
  24#include "hid-ids.h"
  25
  26#define GT683R_BUFFER_SIZE                      8
  27
  28/*
  29 * GT683R_LED_OFF: all LEDs are off
  30 * GT683R_LED_AUDIO: LEDs brightness depends on sound level
  31 * GT683R_LED_BREATHING: LEDs brightness varies at human breathing rate
  32 * GT683R_LED_NORMAL: LEDs are fully on when enabled
  33 */
  34enum gt683r_led_mode {
  35        GT683R_LED_OFF = 0,
  36        GT683R_LED_AUDIO = 2,
  37        GT683R_LED_BREATHING = 3,
  38        GT683R_LED_NORMAL = 5
  39};
  40
  41enum gt683r_panels {
  42        GT683R_LED_BACK = 0,
  43        GT683R_LED_SIDE = 1,
  44        GT683R_LED_FRONT = 2,
  45        GT683R_LED_COUNT,
  46};
  47
  48static const char * const gt683r_panel_names[] = {
  49        "back",
  50        "side",
  51        "front",
  52};
  53
  54struct gt683r_led {
  55        struct hid_device *hdev;
  56        struct led_classdev led_devs[GT683R_LED_COUNT];
  57        struct mutex lock;
  58        struct work_struct work;
  59        enum led_brightness brightnesses[GT683R_LED_COUNT];
  60        enum gt683r_led_mode mode;
  61};
  62
  63static const struct hid_device_id gt683r_led_id[] = {
  64        { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
  65        { }
  66};
  67
  68static void gt683r_brightness_set(struct led_classdev *led_cdev,
  69                                enum led_brightness brightness)
  70{
  71        int i;
  72        struct device *dev = led_cdev->dev->parent;
  73        struct hid_device *hdev = container_of(dev, struct hid_device, dev);
  74        struct gt683r_led *led = hid_get_drvdata(hdev);
  75
  76        for (i = 0; i < GT683R_LED_COUNT; i++) {
  77                if (led_cdev == &led->led_devs[i])
  78                        break;
  79        }
  80
  81        if (i < GT683R_LED_COUNT) {
  82                led->brightnesses[i] = brightness;
  83                schedule_work(&led->work);
  84        }
  85}
  86
  87static ssize_t mode_show(struct device *dev,
  88                                struct device_attribute *attr,
  89                                char *buf)
  90{
  91        u8 sysfs_mode;
  92        struct hid_device *hdev = container_of(dev->parent,
  93                                        struct hid_device, dev);
  94        struct gt683r_led *led = hid_get_drvdata(hdev);
  95
  96        if (led->mode == GT683R_LED_NORMAL)
  97                sysfs_mode = 0;
  98        else if (led->mode == GT683R_LED_AUDIO)
  99                sysfs_mode = 1;
 100        else
 101                sysfs_mode = 2;
 102
 103        return scnprintf(buf, PAGE_SIZE, "%u\n", sysfs_mode);
 104}
 105
 106static ssize_t mode_store(struct device *dev,
 107                                struct device_attribute *attr,
 108                                const char *buf, size_t count)
 109{
 110        u8 sysfs_mode;
 111        struct hid_device *hdev = container_of(dev->parent,
 112                                        struct hid_device, dev);
 113        struct gt683r_led *led = hid_get_drvdata(hdev);
 114
 115
 116        if (kstrtou8(buf, 10, &sysfs_mode) || sysfs_mode > 2)
 117                return -EINVAL;
 118
 119        mutex_lock(&led->lock);
 120
 121        if (sysfs_mode == 0)
 122                led->mode = GT683R_LED_NORMAL;
 123        else if (sysfs_mode == 1)
 124                led->mode = GT683R_LED_AUDIO;
 125        else
 126                led->mode = GT683R_LED_BREATHING;
 127
 128        mutex_unlock(&led->lock);
 129        schedule_work(&led->work);
 130
 131        return count;
 132}
 133
 134static int gt683r_led_snd_msg(struct gt683r_led *led, u8 *msg)
 135{
 136        int ret;
 137
 138        ret = hid_hw_raw_request(led->hdev, msg[0], msg, GT683R_BUFFER_SIZE,
 139                                HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
 140        if (ret != GT683R_BUFFER_SIZE) {
 141                hid_err(led->hdev,
 142                        "failed to send set report request: %i\n", ret);
 143                if (ret < 0)
 144                        return ret;
 145                return -EIO;
 146        }
 147
 148        return 0;
 149}
 150
 151static int gt683r_leds_set(struct gt683r_led *led, u8 leds)
 152{
 153        int ret;
 154        u8 *buffer;
 155
 156        buffer = kzalloc(GT683R_BUFFER_SIZE, GFP_KERNEL);
 157        if (!buffer)
 158                return -ENOMEM;
 159
 160        buffer[0] = 0x01;
 161        buffer[1] = 0x02;
 162        buffer[2] = 0x30;
 163        buffer[3] = leds;
 164        ret = gt683r_led_snd_msg(led, buffer);
 165
 166        kfree(buffer);
 167        return ret;
 168}
 169
 170static int gt683r_mode_set(struct gt683r_led *led, u8 mode)
 171{
 172        int ret;
 173        u8 *buffer;
 174
 175        buffer = kzalloc(GT683R_BUFFER_SIZE, GFP_KERNEL);
 176        if (!buffer)
 177                return -ENOMEM;
 178
 179        buffer[0] = 0x01;
 180        buffer[1] = 0x02;
 181        buffer[2] = 0x20;
 182        buffer[3] = mode;
 183        buffer[4] = 0x01;
 184        ret = gt683r_led_snd_msg(led, buffer);
 185
 186        kfree(buffer);
 187        return ret;
 188}
 189
 190static void gt683r_led_work(struct work_struct *work)
 191{
 192        int i;
 193        u8 leds = 0;
 194        u8 mode;
 195        struct gt683r_led *led = container_of(work, struct gt683r_led, work);
 196
 197        mutex_lock(&led->lock);
 198
 199        for (i = 0; i < GT683R_LED_COUNT; i++) {
 200                if (led->brightnesses[i])
 201                        leds |= BIT(i);
 202        }
 203
 204        if (gt683r_leds_set(led, leds))
 205                goto fail;
 206
 207        if (leds)
 208                mode = led->mode;
 209        else
 210                mode = GT683R_LED_OFF;
 211
 212        gt683r_mode_set(led, mode);
 213fail:
 214        mutex_unlock(&led->lock);
 215}
 216
 217static DEVICE_ATTR_RW(mode);
 218
 219static struct attribute *gt683r_led_attrs[] = {
 220        &dev_attr_mode.attr,
 221        NULL
 222};
 223
 224static const struct attribute_group gt683r_led_group = {
 225        .name = "gt683r",
 226        .attrs = gt683r_led_attrs,
 227};
 228
 229static const struct attribute_group *gt683r_led_groups[] = {
 230        &gt683r_led_group,
 231        NULL
 232};
 233
 234static int gt683r_led_probe(struct hid_device *hdev,
 235                        const struct hid_device_id *id)
 236{
 237        int i;
 238        int ret;
 239        int name_sz;
 240        char *name;
 241        struct gt683r_led *led;
 242
 243        led = devm_kzalloc(&hdev->dev, sizeof(*led), GFP_KERNEL);
 244        if (!led)
 245                return -ENOMEM;
 246
 247        mutex_init(&led->lock);
 248        INIT_WORK(&led->work, gt683r_led_work);
 249
 250        led->mode = GT683R_LED_NORMAL;
 251        led->hdev = hdev;
 252        hid_set_drvdata(hdev, led);
 253
 254        ret = hid_parse(hdev);
 255        if (ret) {
 256                hid_err(hdev, "hid parsing failed\n");
 257                return ret;
 258        }
 259
 260        ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
 261        if (ret) {
 262                hid_err(hdev, "hw start failed\n");
 263                return ret;
 264        }
 265
 266        for (i = 0; i < GT683R_LED_COUNT; i++) {
 267                name_sz = strlen(dev_name(&hdev->dev)) +
 268                                strlen(gt683r_panel_names[i]) + 3;
 269
 270                name = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL);
 271                if (!name) {
 272                        ret = -ENOMEM;
 273                        goto fail;
 274                }
 275
 276                snprintf(name, name_sz, "%s::%s",
 277                                dev_name(&hdev->dev), gt683r_panel_names[i]);
 278                led->led_devs[i].name = name;
 279                led->led_devs[i].max_brightness = 1;
 280                led->led_devs[i].brightness_set = gt683r_brightness_set;
 281                led->led_devs[i].groups = gt683r_led_groups;
 282
 283                ret = led_classdev_register(&hdev->dev, &led->led_devs[i]);
 284                if (ret) {
 285                        hid_err(hdev, "could not register led device\n");
 286                        goto fail;
 287                }
 288        }
 289
 290        return 0;
 291
 292fail:
 293        for (i = i - 1; i >= 0; i--)
 294                led_classdev_unregister(&led->led_devs[i]);
 295        hid_hw_stop(hdev);
 296        return ret;
 297}
 298
 299static void gt683r_led_remove(struct hid_device *hdev)
 300{
 301        int i;
 302        struct gt683r_led *led = hid_get_drvdata(hdev);
 303
 304        for (i = 0; i < GT683R_LED_COUNT; i++)
 305                led_classdev_unregister(&led->led_devs[i]);
 306        flush_work(&led->work);
 307        hid_hw_stop(hdev);
 308}
 309
 310static struct hid_driver gt683r_led_driver = {
 311        .probe = gt683r_led_probe,
 312        .remove = gt683r_led_remove,
 313        .name = "gt683r_led",
 314        .id_table = gt683r_led_id,
 315};
 316
 317module_hid_driver(gt683r_led_driver);
 318
 319MODULE_AUTHOR("Janne Kanniainen");
 320MODULE_DESCRIPTION("MSI GT683R led driver");
 321MODULE_LICENSE("GPL");
 322