brintos

brintos / linux-shallow public Read only

0
0
Text · 6.3 KiB · 29ccb0a Raw
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	&gt683r_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