HID: wacom: leds: use the ledclass instead of custom made sysfs files

The now obsolete sysfs files for LEDs and EKRemote are kept for backward
compatibility.
Both the EKR (read-only) and the regular Cintiqs and Intuos are now
sharing the same led API.

Signed-off-by: Benjamin Tissoires <[email protected]>
Acked-by: Ping Cheng <[email protected]>
Signed-off-by: Jiri Kosina <[email protected]>
This commit is contained in:
Benjamin Tissoires 2016-07-13 18:06:10 +02:00 • committed by Jiri Kosina
commit 97f5541fc0
3 changed files with 209 additions and 10 deletions

View file

@ -91,6 +91,7 @@
#include <linux/mod_devicetable.h>
#include <linux/hid.h>
#include <linux/kfifo.h>
#include <linux/leds.h>
#include <linux/usb/input.h>
#include <linux/power_supply.h>
#include <asm/unaligned.h>
@ -112,8 +113,23 @@ enum wacom_worker {
WACOM_WORKER_REMOTE,
};
struct wacom;
struct wacom_led {
struct led_classdev cdev;
struct led_trigger trigger;
struct wacom *wacom;
unsigned int group;
unsigned int id;
u8 llv;
u8 hlv;
bool held;
};
struct wacom_group_leds {
u8 select; /* status led selector (0..3) */
struct wacom_led *leds;
unsigned int count;
};
struct wacom_battery {
@ -154,9 +170,12 @@ struct wacom {
struct wacom_remote *remote;
struct wacom_leds {
struct wacom_group_leds *groups;
unsigned int count;
u8 llv; /* status led brightness no button (1..127) */
u8 hlv; /* status led brightness button pressed (1..127) */
u8 img_lum; /* OLED matrix display brightness */
u8 max_llv; /* maximum brightness of LED (llv) */
u8 max_hlv; /* maximum brightness of LED (hlv) */
} led;
struct wacom_battery battery;
bool resources;

View file

@ -647,6 +647,9 @@ static int wacom_led_control(struct wacom *wacom)
unsigned char report_id = WAC_CMD_LED_CONTROL;
int buf_size = 9;
if (!hid_get_drvdata(wacom->hdev))
return -ENODEV;
if (!wacom->led.groups)
return -ENOTSUPP;
@ -966,31 +969,194 @@ static int wacom_devm_sysfs_create_group(struct wacom *wacom,
group);
}
static enum led_brightness wacom_leds_brightness_get(struct wacom_led *led)
{
struct wacom *wacom = led->wacom;
if (wacom->led.max_hlv)
return led->hlv * LED_FULL / wacom->led.max_hlv;
if (wacom->led.max_llv)
return led->llv * LED_FULL / wacom->led.max_llv;
/* device doesn't support brightness tuning */
return LED_FULL;
}
static enum led_brightness __wacom_led_brightness_get(struct led_classdev *cdev)
{
struct wacom_led *led = container_of(cdev, struct wacom_led, cdev);
struct wacom *wacom = led->wacom;
if (wacom->led.groups[led->group].select != led->id)
return LED_OFF;
return wacom_leds_brightness_get(led);
}
static int wacom_led_brightness_set(struct led_classdev *cdev,
enum led_brightness brightness)
{
struct wacom_led *led = container_of(cdev, struct wacom_led, cdev);
struct wacom *wacom = led->wacom;
int error;
mutex_lock(&wacom->lock);
if (!wacom->led.groups || (brightness == LED_OFF &&
wacom->led.groups[led->group].select != led->id)) {
error = 0;
goto out;
}
led->llv = wacom->led.llv = wacom->led.max_llv * brightness / LED_FULL;
led->hlv = wacom->led.hlv = wacom->led.max_hlv * brightness / LED_FULL;
wacom->led.groups[led->group].select = led->id;
error = wacom_led_control(wacom);
out:
mutex_unlock(&wacom->lock);
return error;
}
static void wacom_led_readonly_brightness_set(struct led_classdev *cdev,
enum led_brightness brightness)
{
}
static int wacom_led_register_one(struct device *dev, struct wacom *wacom,
struct wacom_led *led, unsigned int group,
unsigned int id, bool read_only)
{
int error;
char *name;
name = devm_kasprintf(dev, GFP_KERNEL,
"%s::wacom-%d.%d",
dev_name(dev),
group,
id);
if (!name)
return -ENOMEM;
led->group = group;
led->id = id;
led->wacom = wacom;
led->llv = wacom->led.llv;
led->hlv = wacom->led.hlv;
led->cdev.name = name;
led->cdev.max_brightness = LED_FULL;
led->cdev.flags = LED_HW_PLUGGABLE;
led->cdev.brightness_get = __wacom_led_brightness_get;
if (!read_only)
led->cdev.brightness_set_blocking = wacom_led_brightness_set;
else
led->cdev.brightness_set = wacom_led_readonly_brightness_set;
error = devm_led_classdev_register(dev, &led->cdev);
if (error) {
hid_err(wacom->hdev,
"failed to register LED %s: %d\n",
led->cdev.name, error);
led->cdev.name = NULL;
return error;
}
return 0;
}
static int wacom_led_groups_alloc_and_register_one(struct device *dev,
struct wacom *wacom,
int group_id, int count,
bool read_only)
{
struct wacom_led *leds;
int i, error;
if (group_id >= wacom->led.count || count <= 0)
return -EINVAL;
if (!devres_open_group(dev, &wacom->led.groups[group_id], GFP_KERNEL))
return -ENOMEM;
leds = devm_kzalloc(dev, sizeof(struct wacom_led) * count, GFP_KERNEL);
if (!leds) {
error = -ENOMEM;
goto err;
}
wacom->led.groups[group_id].leds = leds;
wacom->led.groups[group_id].count = count;
for (i = 0; i < count; i++) {
error = wacom_led_register_one(dev, wacom, &leds[i],
group_id, i, read_only);
if (error)
goto err;
}
devres_remove_group(dev, &wacom->led.groups[group_id]);
return 0;
err:
devres_release_group(dev, &wacom->led.groups[group_id]);
return error;
}
static void wacom_led_groups_release(void *data)
{
struct wacom *wacom = data;
wacom->led.groups = NULL;
wacom->led.count = 0;
}
static int wacom_led_groups_allocate(struct wacom *wacom, int count)
{
struct device *dev = &wacom->hdev->dev;
struct wacom_group_leds *groups;
int error;
groups = devm_kzalloc(&wacom->hdev->dev,
sizeof(struct wacom_group_leds) * count,
groups = devm_kzalloc(dev, sizeof(struct wacom_group_leds) * count,
GFP_KERNEL);
if (!groups)
return -ENOMEM;
error = devm_add_action_or_reset(&wacom->hdev->dev,
wacom_led_groups_release,
wacom);
error = devm_add_action_or_reset(dev, wacom_led_groups_release, wacom);
if (error)
return error;
wacom->led.groups = groups;
wacom->led.count = count;
return 0;
}
static int wacom_leds_alloc_and_register(struct wacom *wacom, int group_count,
int led_per_group, bool read_only)
{
struct device *dev;
int i, error;
if (!wacom->wacom_wac.pad_input)
return -EINVAL;
dev = &wacom->wacom_wac.pad_input->dev;
error = wacom_led_groups_allocate(wacom, group_count);
if (error)
return error;
for (i = 0; i < group_count; i++) {
error = wacom_led_groups_alloc_and_register_one(dev, wacom, i,
led_per_group,
read_only);
if (error)
return error;
}
return 0;
}
@ -1010,9 +1176,11 @@ static int wacom_initialize_leds(struct wacom *wacom)
case INTUOS4L:
wacom->led.llv = 10;
wacom->led.hlv = 20;
wacom->led.max_llv = 127;
wacom->led.max_hlv = 127;
wacom->led.img_lum = 10;
error = wacom_led_groups_allocate(wacom, 1);
error = wacom_leds_alloc_and_register(wacom, 1, 4, false);
if (error) {
hid_err(wacom->hdev,
"cannot create leds err: %d\n", error);
@ -1029,7 +1197,7 @@ static int wacom_initialize_leds(struct wacom *wacom)
wacom->led.hlv = 0;
wacom->led.img_lum = 0;
error = wacom_led_groups_allocate(wacom, 2);
error = wacom_leds_alloc_and_register(wacom, 2, 4, false);
if (error) {
hid_err(wacom->hdev,
"cannot create leds err: %d\n", error);
@ -1047,10 +1215,9 @@ static int wacom_initialize_leds(struct wacom *wacom)
case INTUOSPM:
case INTUOSPL:
wacom->led.llv = 32;
wacom->led.hlv = 0;
wacom->led.img_lum = 0;
wacom->led.max_llv = 96;
error = wacom_led_groups_allocate(wacom, 1);
error = wacom_leds_alloc_and_register(wacom, 1, 4, false);
if (error) {
hid_err(wacom->hdev,
"cannot create leds err: %d\n", error);
@ -1062,6 +1229,8 @@ static int wacom_initialize_leds(struct wacom *wacom)
break;
case REMOTE:
wacom->led.llv = 255;
wacom->led.max_llv = 255;
error = wacom_led_groups_allocate(wacom, 5);
if (error) {
hid_err(wacom->hdev,
@ -1987,6 +2156,12 @@ static int wacom_remote_create_one(struct wacom *wacom, u32 serial,
if (error)
goto fail;
error = wacom_led_groups_alloc_and_register_one(
&remote->remotes[index].input->dev,
wacom, index, 3, true);
if (error)
goto fail;
remote->remotes[index].registered = true;
devres_close_group(dev, &remote->remotes[index]);