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