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