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 = to_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 = to_hid_device(dev->parent);
  93        struct gt683r_led *led = hid_get_drvdata(hdev);
  94
  95        if (led->mode == GT683R_LED_NORMAL)
  96                sysfs_mode = 0;
  97        else if (led->mode == GT683R_LED_AUDIO)
  98                sysfs_mode = 1;
  99        else
 100                sysfs_mode = 2;
 101
 102        return scnprintf(buf, PAGE_SIZE, "%u\n", sysfs_mode);
 103}
 104
 105static ssize_t mode_store(struct device *dev,
 106                                struct device_attribute *attr,
 107                                const char *buf, size_t count)
 108{
 109        u8 sysfs_mode;
 110        struct hid_device *hdev = to_hid_device(dev->parent);
 111        struct gt683r_led *led = hid_get_drvdata(hdev);
 112
 113
 114        if (kstrtou8(buf, 10, &sysfs_mode) || sysfs_mode > 2)
 115                return -EINVAL;
 116
 117        mutex_lock(&led->lock);
 118
 119        if (sysfs_mode == 0)
 120                led->mode = GT683R_LED_NORMAL;
 121        else if (sysfs_mode == 1)
 122                led->mode = GT683R_LED_AUDIO;
 123        else
 124                led->mode = GT683R_LED_BREATHING;
 125
 126        mutex_unlock(&led->lock);
 127        schedule_work(&led->work);
 128
 129        return count;
 130}
 131
 132static int gt683r_led_snd_msg(struct gt683r_led *led, u8 *msg)
 133{
 134        int ret;
 135
 136        ret = hid_hw_raw_request(led->hdev, msg[0], msg, GT683R_BUFFER_SIZE,
 137                                HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
 138        if (ret != GT683R_BUFFER_SIZE) {
 139                hid_err(led->hdev,
 140                        "failed to send set report request: %i\n", ret);
 141                if (ret < 0)
 142                        return ret;
 143                return -EIO;
 144        }
 145
 146        return 0;
 147}
 148
 149static int gt683r_leds_set(struct gt683r_led *led, u8 leds)
 150{
 151        int ret;
 152        u8 *buffer;
 153
 154        buffer = kzalloc(GT683R_BUFFER_SIZE, GFP_KERNEL);
 155        if (!buffer)
 156                return -ENOMEM;
 157
 158        buffer[0] = 0x01;
 159        buffer[1] = 0x02;
 160        buffer[2] = 0x30;
 161        buffer[3] = leds;
 162        ret = gt683r_led_snd_msg(led, buffer);
 163
 164        kfree(buffer);
 165        return ret;
 166}
 167
 168static int gt683r_mode_set(struct gt683r_led *led, u8 mode)
 169{
 170        int ret;
 171        u8 *buffer;
 172
 173        buffer = kzalloc(GT683R_BUFFER_SIZE, GFP_KERNEL);
 174        if (!buffer)
 175                return -ENOMEM;
 176
 177        buffer[0] = 0x01;
 178        buffer[1] = 0x02;
 179        buffer[2] = 0x20;
 180        buffer[3] = mode;
 181        buffer[4] = 0x01;
 182        ret = gt683r_led_snd_msg(led, buffer);
 183
 184        kfree(buffer);
 185        return ret;
 186}
 187
 188static void gt683r_led_work(struct work_struct *work)
 189{
 190        int i;
 191        u8 leds = 0;
 192        u8 mode;
 193        struct gt683r_led *led = container_of(work, struct gt683r_led, work);
 194
 195        mutex_lock(&led->lock);
 196
 197        for (i = 0; i < GT683R_LED_COUNT; i++) {
 198                if (led->brightnesses[i])
 199                        leds |= BIT(i);
 200        }
 201
 202        if (gt683r_leds_set(led, leds))
 203                goto fail;
 204
 205        if (leds)
 206                mode = led->mode;
 207        else
 208                mode = GT683R_LED_OFF;
 209
 210        gt683r_mode_set(led, mode);
 211fail:
 212        mutex_unlock(&led->lock);
 213}
 214
 215static DEVICE_ATTR_RW(mode);
 216
 217static struct attribute *gt683r_led_attrs[] = {
 218        &dev_attr_mode.attr,
 219        NULL
 220};
 221
 222static const struct attribute_group gt683r_led_group = {
 223        .name = "gt683r",
 224        .attrs = gt683r_led_attrs,
 225};
 226
 227static const struct attribute_group *gt683r_led_groups[] = {
 228        &gt683r_led_group,
 229        NULL
 230};
 231
 232static int gt683r_led_probe(struct hid_device *hdev,
 233                        const struct hid_device_id *id)
 234{
 235        int i;
 236        int ret;
 237        int name_sz;
 238        char *name;
 239        struct gt683r_led *led;
 240
 241        led = devm_kzalloc(&hdev->dev, sizeof(*led), GFP_KERNEL);
 242        if (!led)
 243                return -ENOMEM;
 244
 245        mutex_init(&led->lock);
 246        INIT_WORK(&led->work, gt683r_led_work);
 247
 248        led->mode = GT683R_LED_NORMAL;
 249        led->hdev = hdev;
 250        hid_set_drvdata(hdev, led);
 251
 252        ret = hid_parse(hdev);
 253        if (ret) {
 254                hid_err(hdev, "hid parsing failed\n");
 255                return ret;
 256        }
 257
 258        ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
 259        if (ret) {
 260                hid_err(hdev, "hw start failed\n");
 261                return ret;
 262        }
 263
 264        for (i = 0; i < GT683R_LED_COUNT; i++) {
 265                name_sz = strlen(dev_name(&hdev->dev)) +
 266                                strlen(gt683r_panel_names[i]) + 3;
 267
 268                name = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL);
 269                if (!name) {
 270                        ret = -ENOMEM;
 271                        goto fail;
 272                }
 273
 274                snprintf(name, name_sz, "%s::%s",
 275                                dev_name(&hdev->dev), gt683r_panel_names[i]);
 276                led->led_devs[i].name = name;
 277                led->led_devs[i].max_brightness = 1;
 278                led->led_devs[i].brightness_set = gt683r_brightness_set;
 279                led->led_devs[i].groups = gt683r_led_groups;
 280
 281                ret = led_classdev_register(&hdev->dev, &led->led_devs[i]);
 282                if (ret) {
 283                        hid_err(hdev, "could not register led device\n");
 284                        goto fail;
 285                }
 286        }
 287
 288        return 0;
 289
 290fail:
 291        for (i = i - 1; i >= 0; i--)
 292                led_classdev_unregister(&led->led_devs[i]);
 293        hid_hw_stop(hdev);
 294        return ret;
 295}
 296
 297static void gt683r_led_remove(struct hid_device *hdev)
 298{
 299        int i;
 300        struct gt683r_led *led = hid_get_drvdata(hdev);
 301
 302        for (i = 0; i < GT683R_LED_COUNT; i++)
 303                led_classdev_unregister(&led->led_devs[i]);
 304        flush_work(&led->work);
 305        hid_hw_stop(hdev);
 306}
 307
 308static struct hid_driver gt683r_led_driver = {
 309        .probe = gt683r_led_probe,
 310        .remove = gt683r_led_remove,
 311        .name = "gt683r_led",
 312        .id_table = gt683r_led_id,
 313};
 314
 315module_hid_driver(gt683r_led_driver);
 316
 317MODULE_AUTHOR("Janne Kanniainen");
 318MODULE_DESCRIPTION("MSI GT683R led driver");
 319MODULE_LICENSE("GPL");
 320