2014-06-05 21:29:41

by Janne Kanniainen

[permalink] [raw]
Subject: [PATCH v2] leds: USB: Add support for MSI GT683R led panels

This driver adds support for USB controlled led panels that exist in MSI GT683R laptop.

Changes in v2:
- sorted headers to alphabetic order
- using devm_kzalloc
- using BIT(n)
- using usb_control_msg instead of usb_submit_urb
- removing unneeded code

Signed-off-by: Janne Kanniainen <[email protected]>
---
drivers/leds/Kconfig | 6 ++
drivers/leds/Makefile | 1 +
drivers/leds/leds-gt683r.c | 158 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 165 insertions(+)
create mode 100644 drivers/leds/leds-gt683r.c

diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 6de9dfb..2cffa0c 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -487,6 +487,12 @@ config LEDS_BLINKM
This option enables support for the BlinkM RGB LED connected
through I2C. Say Y to enable support for the BlinkM LED.

+config LEDS_GT683R
+ tristate "LED support for the MSI GT683R"
+ depends on LEDS_CLASS && USB
+ help
+ This option enables support for the MSI GT683R LEDS
+
comment "LED Triggers"
source "drivers/leds/trigger/Kconfig"

diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 3cd76db..af5fb4e 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -54,6 +54,7 @@ obj-$(CONFIG_LEDS_ASIC3) += leds-asic3.o
obj-$(CONFIG_LEDS_MAX8997) += leds-max8997.o
obj-$(CONFIG_LEDS_LM355x) += leds-lm355x.o
obj-$(CONFIG_LEDS_BLINKM) += leds-blinkm.o
+obj-$(CONFIG_LEDS_GT683R) += leds-gt683r.o

# LED SPI Drivers
obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o
diff --git a/drivers/leds/leds-gt683r.c b/drivers/leds/leds-gt683r.c
new file mode 100644
index 0000000..5a204ea
--- /dev/null
+++ b/drivers/leds/leds-gt683r.c
@@ -0,0 +1,158 @@
+/*
+ * MSI GT683R led driver
+ *
+ * Copyright (c) 2014 Janne Kanniainen <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+
+#define USB_DEVICE_ID_MSI_GT683R_LED_PANEL 0xff00
+#define USB_VENDOR_ID_MSI 0x1770
+
+#define GT683R_LED_BACK BIT(0)
+#define GT683R_LED_SIDE BIT(1)
+#define GT683R_LED_FRONT BIT(2)
+#define GT683R_LED_ALL (GT683R_LED_BACK | GT683R_LED_SIDE | GT683R_LED_FRONT)
+
+#define GT683R_LED_OFF 0
+#define GT683R_LED_AUDIO 2
+#define GT683R_LED_BREATHING 3
+#define GT683R_LED_NORMAL 5
+
+struct gt683r_led {
+ struct usb_device *usb_dev;
+ struct usb_endpoint_descriptor *ep;
+ struct led_classdev led_dev;
+ struct mutex lock;
+ struct work_struct work;
+ enum led_brightness brightness;
+};
+
+static const struct usb_device_id gt683r_led_id[] = {
+ { USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
+ { }
+};
+
+static const u64 gt683r_led_select_leds = 0x300201ULL;
+static const u64 gt683r_led_select_type = 0x100200201ULL;
+
+static void gt683r_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct gt683r_led *led =
+ container_of(led_cdev, struct gt683r_led, led_dev);
+
+ mutex_lock(&led->lock);
+
+ led->brightness = brightness;
+
+ schedule_work(&led->work);
+}
+
+static int gt683r_led_snd_msg(struct gt683r_led *led, u64 msg)
+{
+ int result = usb_control_msg(led->usb_dev,
+ usb_sndctrlpipe(led->usb_dev,
+ led->ep->bEndpointAddress),
+ 0x09, 0x22, 0x0301, 0x0000,
+ &msg, sizeof(u64), USB_CTRL_SET_TIMEOUT);
+ if (result)
+ dev_err(&led->usb_dev->dev,
+ "Failed to send control message: %i\n", result);
+
+ return result;
+}
+
+static void gt683r_led_set(struct gt683r_led *led, char type)
+{
+ gt683r_led_snd_msg(led, gt683r_led_select_leds |
+ (GT683R_LED_ALL << 24));
+ gt683r_led_snd_msg(led, gt683r_led_select_type |
+ (type << 24));
+}
+
+static void gt683r_led_work(struct work_struct *work)
+{
+ struct gt683r_led *led =
+ container_of(work, struct gt683r_led, work);
+
+ if (led->brightness)
+ gt683r_led_set(led, GT683R_LED_NORMAL);
+ else
+ gt683r_led_set(led, GT683R_LED_OFF);
+
+ mutex_unlock(&led->lock);
+}
+
+static struct led_classdev gt683r_led_dev = {
+ .name = "gt683r-led",
+ .brightness_set = gt683r_brightness_set,
+ .max_brightness = 1,
+ .flags = LED_CORE_SUSPENDRESUME,
+};
+
+static int gt683r_led_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ int ret = 0;
+ struct gt683r_led *led =
+ devm_kzalloc(&intf->dev, sizeof(struct gt683r_led), GFP_KERNEL);
+ if (!led)
+ return -ENOMEM;
+
+ led->usb_dev = interface_to_usbdev(intf);
+ usb_set_intfdata(intf, led);
+
+ led->ep = &led->usb_dev->ep0.desc;
+ if (!usb_endpoint_xfer_control(led->ep) ||
+ usb_endpoint_maxp(led->ep) != sizeof(u64))
+ return -EINVAL;
+
+ led->led_dev = gt683r_led_dev;
+ ret = led_classdev_register(&led->usb_dev->dev, &led->led_dev);
+ if (ret) {
+ dev_err(&led->usb_dev->dev, "Could not register led_classdev\n");
+ return ret;
+ }
+
+ mutex_init(&led->lock);
+ INIT_WORK(&led->work, gt683r_led_work);
+
+ return 0;
+}
+
+static void gt683r_led_disconnect(struct usb_interface *intf)
+{
+ struct gt683r_led *led = usb_get_intfdata(intf);
+
+ led_classdev_unregister(&led->led_dev);
+ cancel_work_sync(&led->work);
+}
+
+static struct usb_driver gt683r_led_driver = {
+ .probe = gt683r_led_probe,
+ .disconnect = gt683r_led_disconnect,
+ .name = "GT683R Led Driver",
+ .id_table = gt683r_led_id,
+};
+
+module_usb_driver(gt683r_led_driver);
+
+MODULE_AUTHOR("Janne Kanniainen");
+MODULE_DESCRIPTION("MSI GT683R led driver");
+MODULE_LICENSE("GPL");
--
1.9.2


2014-06-06 09:48:29

by Johan Hovold

[permalink] [raw]
Subject: Re: [PATCH v2] leds: USB: Add support for MSI GT683R led panels

[ +CC: Jiri, linux-input, linux-usb (again) ]

First of all, please reply to the original thread and make sure to not
drop people or lists from CC.

On Fri, Jun 06, 2014 at 12:29:06AM +0300, Janne Kanniainen wrote:
> This driver adds support for USB controlled led panels that exist in
> MSI GT683R laptop.

I had a change to look at bit closer at this today and it turns out this
is a HID device and as such is already claimed by the HID driver.

There was a quirk added for this particular VID/PID by commit
620ae90ed8ca ("HID: usbhid: quirk for MSI GX680R led panel") in order to
avoid a 10s delay at boot due to missing report descriptors:

https://bugzilla.redhat.com/show_bug.cgi?id=907221
http://www.spinics.net/lists/linux-usb/msg54756.html

Since this is a HID device (and the control requests you're doing are
Set_Report requests) you should probably implement this as a specific
HID driver if there's no generic support you can use directly.

I've added Jiri Kosina (and linux-input) who knows a lot more about the
HID layer and how this is supposed to be implemented as CC.

> Changes in v2:
> - sorted headers to alphabetic order
> - using devm_kzalloc
> - using BIT(n)
> - using usb_control_msg instead of usb_submit_urb
> - removing unneeded code

There are still some issues in the code below, but I'll only point out
the more problematic ones for now.

> Signed-off-by: Janne Kanniainen <[email protected]>
> ---
> drivers/leds/Kconfig | 6 ++
> drivers/leds/Makefile | 1 +
> drivers/leds/leds-gt683r.c | 158 +++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 165 insertions(+)
> create mode 100644 drivers/leds/leds-gt683r.c
>
> diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
> index 6de9dfb..2cffa0c 100644
> --- a/drivers/leds/Kconfig
> +++ b/drivers/leds/Kconfig
> @@ -487,6 +487,12 @@ config LEDS_BLINKM
> This option enables support for the BlinkM RGB LED connected
> through I2C. Say Y to enable support for the BlinkM LED.
>
> +config LEDS_GT683R
> + tristate "LED support for the MSI GT683R"
> + depends on LEDS_CLASS && USB
> + help
> + This option enables support for the MSI GT683R LEDS
> +
> comment "LED Triggers"
> source "drivers/leds/trigger/Kconfig"
>
> diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
> index 3cd76db..af5fb4e 100644
> --- a/drivers/leds/Makefile
> +++ b/drivers/leds/Makefile
> @@ -54,6 +54,7 @@ obj-$(CONFIG_LEDS_ASIC3) += leds-asic3.o
> obj-$(CONFIG_LEDS_MAX8997) += leds-max8997.o
> obj-$(CONFIG_LEDS_LM355x) += leds-lm355x.o
> obj-$(CONFIG_LEDS_BLINKM) += leds-blinkm.o
> +obj-$(CONFIG_LEDS_GT683R) += leds-gt683r.o
>
> # LED SPI Drivers
> obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o
> diff --git a/drivers/leds/leds-gt683r.c b/drivers/leds/leds-gt683r.c
> new file mode 100644
> index 0000000..5a204ea
> --- /dev/null
> +++ b/drivers/leds/leds-gt683r.c
> @@ -0,0 +1,158 @@
> +/*
> + * MSI GT683R led driver
> + *
> + * Copyright (c) 2014 Janne Kanniainen <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/leds.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/usb.h>
> +
> +#define USB_DEVICE_ID_MSI_GT683R_LED_PANEL 0xff00
> +#define USB_VENDOR_ID_MSI 0x1770
> +
> +#define GT683R_LED_BACK BIT(0)
> +#define GT683R_LED_SIDE BIT(1)
> +#define GT683R_LED_FRONT BIT(2)
> +#define GT683R_LED_ALL (GT683R_LED_BACK | GT683R_LED_SIDE | GT683R_LED_FRONT)
> +
> +#define GT683R_LED_OFF 0
> +#define GT683R_LED_AUDIO 2
> +#define GT683R_LED_BREATHING 3
> +#define GT683R_LED_NORMAL 5
> +
> +struct gt683r_led {
> + struct usb_device *usb_dev;
> + struct usb_endpoint_descriptor *ep;
> + struct led_classdev led_dev;
> + struct mutex lock;
> + struct work_struct work;
> + enum led_brightness brightness;
> +};
> +
> +static const struct usb_device_id gt683r_led_id[] = {
> + { USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
> + { }
> +};
> +
> +static const u64 gt683r_led_select_leds = 0x300201ULL;
> +static const u64 gt683r_led_select_type = 0x100200201ULL;

No, you'd still want to use byte arrays for this in order to handle
endianess (if you are to keep these at all).

For arrays you can use the ARRAY_SIZE() macro if that was the reason for
this change. I should have mentioned that when I pointed out that you
cannot use strlen().

Where did you get these (HID report) values from by the way?

> +
> +static void gt683r_brightness_set(struct led_classdev *led_cdev,
> + enum led_brightness brightness)
> +{
> + struct gt683r_led *led =
> + container_of(led_cdev, struct gt683r_led, led_dev);
> +
> + mutex_lock(&led->lock);

You cannot grab a mutex here since this function can be called from
interrupt context (if I remember correctly). Either way, you shouldn't
be holding the lock until the work task has finished...

> +
> + led->brightness = brightness;
> +
> + schedule_work(&led->work);
> +}
> +
> +static int gt683r_led_snd_msg(struct gt683r_led *led, u64 msg)
> +{
> + int result = usb_control_msg(led->usb_dev,
> + usb_sndctrlpipe(led->usb_dev,
> + led->ep->bEndpointAddress),

No need to store the endpoint in the led struct as the address of
endpoint 0 is 0.

> + 0x09, 0x22, 0x0301, 0x0000,
> + &msg, sizeof(u64), USB_CTRL_SET_TIMEOUT);

You cannot use a stack allocated buffer (msg) for the data transfer as
some platforms can't do DMA from the stack.

> + if (result)
> + dev_err(&led->usb_dev->dev,
> + "Failed to send control message: %i\n", result);
> +
> + return result;
> +}
> +
> +static void gt683r_led_set(struct gt683r_led *led, char type)
> +{
> + gt683r_led_snd_msg(led, gt683r_led_select_leds |
> + (GT683R_LED_ALL << 24));
> + gt683r_led_snd_msg(led, gt683r_led_select_type |
> + (type << 24));
> +}
> +
> +static void gt683r_led_work(struct work_struct *work)
> +{
> + struct gt683r_led *led =
> + container_of(work, struct gt683r_led, work);
> +
> + if (led->brightness)
> + gt683r_led_set(led, GT683R_LED_NORMAL);
> + else
> + gt683r_led_set(led, GT683R_LED_OFF);
> +
> + mutex_unlock(&led->lock);
> +}
> +
> +static struct led_classdev gt683r_led_dev = {
> + .name = "gt683r-led",
> + .brightness_set = gt683r_brightness_set,
> + .max_brightness = 1,
> + .flags = LED_CORE_SUSPENDRESUME,
> +};
> +
> +static int gt683r_led_probe(struct usb_interface *intf,
> + const struct usb_device_id *id)
> +{
> + int ret = 0;
> + struct gt683r_led *led =
> + devm_kzalloc(&intf->dev, sizeof(struct gt683r_led), GFP_KERNEL);

Again, please split definition and allocation.

> + if (!led)
> + return -ENOMEM;
> +
> + led->usb_dev = interface_to_usbdev(intf);
> + usb_set_intfdata(intf, led);
> +
> + led->ep = &led->usb_dev->ep0.desc;
> + if (!usb_endpoint_xfer_control(led->ep) ||
> + usb_endpoint_maxp(led->ep) != sizeof(u64))
> + return -EINVAL;

EP0 is a control endpoint, and you probably don't need the
max-packet-size test either.

> +
> + led->led_dev = gt683r_led_dev;
> + ret = led_classdev_register(&led->usb_dev->dev, &led->led_dev);
> + if (ret) {
> + dev_err(&led->usb_dev->dev, "Could not register led_classdev\n");
> + return ret;
> + }
> +
> + mutex_init(&led->lock);
> + INIT_WORK(&led->work, gt683r_led_work);
> +
> + return 0;
> +}
> +
> +static void gt683r_led_disconnect(struct usb_interface *intf)
> +{
> + struct gt683r_led *led = usb_get_intfdata(intf);
> +
> + led_classdev_unregister(&led->led_dev);
> + cancel_work_sync(&led->work);
> +}
> +
> +static struct usb_driver gt683r_led_driver = {
> + .probe = gt683r_led_probe,
> + .disconnect = gt683r_led_disconnect,
> + .name = "GT683R Led Driver",
> + .id_table = gt683r_led_id,
> +};
> +
> +module_usb_driver(gt683r_led_driver);
> +
> +MODULE_AUTHOR("Janne Kanniainen");
> +MODULE_DESCRIPTION("MSI GT683R led driver");
> +MODULE_LICENSE("GPL");

Johan

2014-06-07 10:12:42

by Janne Kanniainen

[permalink] [raw]
Subject: Re: [PATCH v2] leds: USB: Add support for MSI GT683R led panels

> First of all, please reply to the original thread and make sure to not
> drop people or lists from CC.

Sorry this is my first patch and i didn't know that. Now I know.

> For arrays you can use the ARRAY_SIZE() macro if that was the reason for
> this change. I should have mentioned that when I pointed out that you
> cannot use strlen().

That wasn't the reason. I just thought it might be better to use u64
than char[8]. I know why I can't use strlen and that was only careless
error. And there was lot of them :( I will be more careful next time.

> Where did you get these (HID report) values from by the way?

I got them by reverse engineering.

>> +
>> +static void gt683r_brightness_set(struct led_classdev *led_cdev,
>> + enum led_brightness brightness)
>> +{
>> + struct gt683r_led *led =
>> + container_of(led_cdev, struct gt683r_led, led_dev);
>> +
>> + mutex_lock(&led->lock);
>
> You cannot grab a mutex here since this function can be called from
> interrupt context (if I remember correctly). Either way, you shouldn't
> be holding the lock until the work task has finished...

I thought use asked me to put some lock there:

>> +
>> +static void gt683r_brightness_set(struct led_classdev *led_cdev,
>> + enum led_brightness brightness)
>> +{
>> + struct gt683r_led *led =
>> + container_of(led_cdev, struct gt683r_led, led_dev);
>> +
>> + led->brightness = brightness;
>
> Missing locking?

Janne

2014-06-09 11:43:01

by Johan Hovold

[permalink] [raw]
Subject: Re: [PATCH v2] leds: USB: Add support for MSI GT683R led panels

On Sat, Jun 07, 2014 at 01:12:39PM +0300, Janne Kanniainen wrote:
> > First of all, please reply to the original thread and make sure to not
> > drop people or lists from CC.
>
> Sorry this is my first patch and i didn't know that. Now I know.
>
> > For arrays you can use the ARRAY_SIZE() macro if that was the reason for
> > this change. I should have mentioned that when I pointed out that you
> > cannot use strlen().
>
> That wasn't the reason. I just thought it might be better to use u64
> than char[8]. I know why I can't use strlen and that was only careless
> error. And there was lot of them :( I will be more careful next time.

No problem, that's what review is for, and the second version was much
cleaner even if there were still a few issues (some hard to know about,
such as the DMA from stack issue, which is also a very common error).

> > Where did you get these (HID report) values from by the way?
>
> I got them by reverse engineering.

Traffic sniffing?

> >> +
> >> +static void gt683r_brightness_set(struct led_classdev *led_cdev,
> >> + enum led_brightness brightness)
> >> +{
> >> + struct gt683r_led *led =
> >> + container_of(led_cdev, struct gt683r_led, led_dev);
> >> +
> >> + mutex_lock(&led->lock);
> >
> > You cannot grab a mutex here since this function can be called from
> > interrupt context (if I remember correctly). Either way, you shouldn't
> > be holding the lock until the work task has finished...
>
> I thought use asked me to put some lock there:
>
> >> +
> >> +static void gt683r_brightness_set(struct led_classdev *led_cdev,
> >> + enum led_brightness brightness)
> >> +{
> >> + struct gt683r_led *led =
> >> + container_of(led_cdev, struct gt683r_led, led_dev);
> >> +
> >> + led->brightness = brightness;
> >
> > Missing locking?

I asked if locking was missing and did not specify how you should be
adding it. ;)

In fact, it seems you can get away with not adding any locking here.
Just do the (mutex) locking in gt683r_led_set (or gt683r_led_work).

Johan

2014-06-10 21:10:58

by Janne Kanniainen

[permalink] [raw]
Subject: Re: [PATCH v2] leds: USB: Add support for MSI GT683R led panels

>> > Where did you get these (HID report) values from by the way?
>>
>> I got them by reverse engineering.
>
> Traffic sniffing?
>

Yes.


> In fact, it seems you can get away with not adding any locking here.
> Just do the (mutex) locking in gt683r_led_set (or gt683r_led_work).

Ok I will fix it.

Janne

2014-06-10 21:22:13

by Janne Kanniainen

[permalink] [raw]
Subject: [PATCH v3] leds: USB: HID: Add support for MSI GT683R led panels

This driver adds support for USB controlled led panels that exists in MSI GT683R laptop

Changes in v2:
- sorted headers to alphabetic order
- using devm_kzalloc
- using BIT(n)
- using usb_control_msg instead of usb_submit_urb
- removing unneeded code

Changes in v3:
- implemented as HID device
- some cleanups and bug fixes

Signed-off-by: Janne Kanniainen <[email protected]>
---
drivers/hid/Kconfig | 6 ++
drivers/hid/Makefile | 1 +
drivers/hid/hid-core.c | 1 +
drivers/hid/hid-gt683r.c | 186 ++++++++++++++++++++++++++++++++++++++++
drivers/hid/hid-ids.h | 1 +
drivers/hid/usbhid/hid-quirks.c | 1 +
6 files changed, 196 insertions(+)
create mode 100644 drivers/hid/hid-gt683r.c

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 7af9d0b..6ecc527 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -210,6 +210,12 @@ config DRAGONRISE_FF
Say Y here if you want to enable force feedback support for DragonRise Inc.
game controllers.

+config HID_GT683R
+ tristate "LED support for the MSI GT683R"
+ depends on LEDS_CLASS && USB_HID
+ ---help---
+ Say Y here if you want to enable support for the MSI GT683R LEDS
+
config HID_EMS_FF
tristate "EMS Production Inc. force feedback support"
depends on HID
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index fc712dd..111304e 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -44,6 +44,7 @@ obj-$(CONFIG_HID_CHICONY) += hid-chicony.o
obj-$(CONFIG_HID_CP2112) += hid-cp2112.o
obj-$(CONFIG_HID_CYPRESS) += hid-cypress.o
obj-$(CONFIG_HID_DRAGONRISE) += hid-dr.o
+obj-$(CONFIG_HID_GT683R) += hid-gt683r.o
obj-$(CONFIG_HID_EMS_FF) += hid-emsff.o
obj-$(CONFIG_HID_ELECOM) += hid-elecom.o
obj-$(CONFIG_HID_ELO) += hid-elo.o
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index da52279..ec88fdb 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1827,6 +1827,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_OFFICE_KB) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) },
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_1) },
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_2) },
diff --git a/drivers/hid/hid-gt683r.c b/drivers/hid/hid-gt683r.c
new file mode 100644
index 0000000..4baaa36
--- /dev/null
+++ b/drivers/hid/hid-gt683r.c
@@ -0,0 +1,186 @@
+/*
+ * MSI GT683R led driver
+ *
+ * Copyright (c) 2014 Janne Kanniainen <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/hid.h>
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+#define GT683R_LED_BACK BIT(0)
+#define GT683R_LED_SIDE BIT(1)
+#define GT683R_LED_FRONT BIT(2)
+#define GT683R_LED_ALL (GT683R_LED_BACK | GT683R_LED_SIDE | GT683R_LED_FRONT)
+
+#define GT683R_LED_OFF 0
+#define GT683R_LED_AUDIO 2
+#define GT683R_LED_BREATHING 3
+#define GT683R_LED_NORMAL 5
+
+#define GT683R_BUFFER_SIZE 8
+
+struct gt683r_led {
+ struct hid_device *hdev;
+ struct led_classdev led_dev;
+ struct mutex lock;
+ struct work_struct work;
+ enum led_brightness brightness;
+};
+
+static const struct hid_device_id gt683r_led_id[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
+ { }
+};
+
+static const char gt683r_led_select_leds[GT683R_BUFFER_SIZE] = { 0x01, 0x02, 0x30, 0x00,
+ 0x00, 0x00, 0x00, 0x00 };
+static const char gt683r_led_select_type[GT683R_BUFFER_SIZE] = { 0x01, 0x02, 0x20, 0x00,
+ 0x01, 0x00, 0x00, 0x00 };
+
+static void gt683r_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct gt683r_led *led =
+ container_of(led_cdev, struct gt683r_led, led_dev);
+
+ led->brightness = brightness;
+
+ schedule_work(&led->work);
+}
+
+static int gt683r_led_snd_msg(struct gt683r_led *led, char *msg)
+{
+ int ret;
+
+ ret = hid_hw_raw_request(led->hdev, 0x01, msg, GT683R_BUFFER_SIZE,
+ HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
+ if (ret < 0) {
+ hid_err(led->hdev,
+ "failed to send set report request: %i\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void gt683r_led_set(struct gt683r_led *led, char type)
+{
+ char *buffer;
+
+ buffer = kmalloc(GT683R_BUFFER_SIZE, GFP_KERNEL);
+ if (!buffer) {
+ hid_err(led->hdev, "can\'t allocate buffer\n");
+ return;
+ }
+
+ memcpy(buffer, gt683r_led_select_leds, GT683R_BUFFER_SIZE);
+ buffer[3] = GT683R_LED_ALL;
+ gt683r_led_snd_msg(led, buffer);
+
+ memcpy(buffer, gt683r_led_select_type, GT683R_BUFFER_SIZE);
+ buffer[3] = type;
+ gt683r_led_snd_msg(led, buffer);
+
+ kfree(buffer);
+}
+
+static void gt683r_led_work(struct work_struct *work)
+{
+ struct gt683r_led *led =
+ container_of(work, struct gt683r_led, work);
+
+ mutex_lock(&led->lock);
+
+ if (led->brightness)
+ gt683r_led_set(led, GT683R_LED_NORMAL);
+ else
+ gt683r_led_set(led, GT683R_LED_OFF);
+
+ mutex_unlock(&led->lock);
+}
+
+static struct led_classdev gt683r_led_dev = {
+ .name = "gt683r-led",
+ .brightness_set = gt683r_brightness_set,
+ .max_brightness = 1,
+ .flags = LED_CORE_SUSPENDRESUME,
+};
+
+static int gt683r_led_probe(struct hid_device *hdev,
+ const struct hid_device_id *id)
+{
+ int ret;
+ struct gt683r_led *led;
+
+ led = devm_kzalloc(&hdev->dev, sizeof(struct gt683r_led), GFP_KERNEL);
+ if (!led)
+ return -ENOMEM;
+
+ led->hdev = hdev;
+ hid_set_drvdata(hdev, led);
+
+ ret = hid_parse(hdev);
+ if (ret) {
+ hid_err(hdev, "hid parsing failed\n");
+ goto fail;
+ }
+
+ ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
+ if (ret) {
+ hid_err(hdev, "hw start failed\n");
+ goto fail;
+ }
+
+ led->led_dev = gt683r_led_dev;
+ ret = led_classdev_register(&hdev->dev, &led->led_dev);
+ if (ret) {
+ hid_err(hdev, "could not register led device\n");
+ goto stop;
+ }
+
+ mutex_init(&led->lock);
+ INIT_WORK(&led->work, gt683r_led_work);
+
+ return 0;
+stop:
+ hid_hw_stop(hdev);
+fail:
+ return ret;
+}
+
+static void gt683r_led_remove(struct hid_device *hdev)
+{
+ struct gt683r_led *led = hid_get_drvdata(hdev);
+
+ led_classdev_unregister(&led->led_dev);
+ cancel_work_sync(&led->work);
+ hid_hw_stop(hdev);
+}
+
+static struct hid_driver gt683r_led_driver = {
+ .probe = gt683r_led_probe,
+ .remove = gt683r_led_remove,
+ .name = "gt683r_led",
+ .id_table = gt683r_led_id,
+};
+
+module_hid_driver(gt683r_led_driver);
+
+MODULE_AUTHOR("Janne Kanniainen");
+MODULE_DESCRIPTION("MSI GT683R led driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 34bb220..e2097f7 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -641,6 +641,7 @@
#define USB_DEVICE_ID_GENIUS_KB29E 0x3004

#define USB_VENDOR_ID_MSI 0x1770
+#define USB_DEVICE_ID_MSI_GT683R_LED_PANEL 0xff00
#define USB_DEVICE_ID_MSI_GX680R_LED_PANEL 0xff00

#define USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR 0x0400
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
index 8e4ddb3..927e7be 100644
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -73,6 +73,7 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET },
+ { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GX680R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_NEXIO, USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_MOUSE, HID_QUIRK_NO_INIT_REPORTS },
--
1.9.2

2014-06-11 11:25:53

by Jiri Kosina

[permalink] [raw]
Subject: Re: [PATCH v3] leds: USB: HID: Add support for MSI GT683R led panels

On Wed, 11 Jun 2014, Janne Kanniainen wrote:

> This driver adds support for USB controlled led panels that exists in MSI GT683R laptop
>
> Changes in v2:
> - sorted headers to alphabetic order
> - using devm_kzalloc
> - using BIT(n)
> - using usb_control_msg instead of usb_submit_urb
> - removing unneeded code
>
> Changes in v3:
> - implemented as HID device
> - some cleanups and bug fixes
>
> Signed-off-by: Janne Kanniainen <[email protected]>

Thanks for the driver. Johan, as you did a very good job reviewing this
before I managed to get to it, I'd be glad to put your Reviewed-by: to the
commit before commiting it, if you are going to provide it.

Thanks,

--
Jiri Kosina
SUSE Labs

2014-06-11 14:06:27

by Johan Hovold

[permalink] [raw]
Subject: Re: [PATCH v3] leds: USB: HID: Add support for MSI GT683R led panels

On Wed, Jun 11, 2014 at 12:21:39AM +0300, Janne Kanniainen wrote:
> This driver adds support for USB controlled led panels that exists in MSI GT683R laptop

Can you break this line by 72 columns or so as well?

> Changes in v2:
> - sorted headers to alphabetic order
> - using devm_kzalloc
> - using BIT(n)
> - using usb_control_msg instead of usb_submit_urb
> - removing unneeded code
>
> Changes in v3:
> - implemented as HID device
> - some cleanups and bug fixes

Thanks for the update, Janne. It looks really good now.

Please put the changelog entries after the cut-off line below as it's
not really needed in the git logs.

You should also always run your patches through scripts/checkpatch.pl
before submitting. It currently reports 8 warnings of which you should
fix all but possible the ones about device id entries exceeding 80 chars
(which is usually considered acceptable).

A few more comments follow below.

> Signed-off-by: Janne Kanniainen <[email protected]>
> ---
> drivers/hid/Kconfig | 6 ++
> drivers/hid/Makefile | 1 +
> drivers/hid/hid-core.c | 1 +
> drivers/hid/hid-gt683r.c | 186 ++++++++++++++++++++++++++++++++++++++++
> drivers/hid/hid-ids.h | 1 +
> drivers/hid/usbhid/hid-quirks.c | 1 +
> 6 files changed, 196 insertions(+)
> create mode 100644 drivers/hid/hid-gt683r.c
>
> diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
> index 7af9d0b..6ecc527 100644
> --- a/drivers/hid/Kconfig
> +++ b/drivers/hid/Kconfig
> @@ -210,6 +210,12 @@ config DRAGONRISE_FF
> Say Y here if you want to enable force feedback support for DragonRise Inc.
> game controllers.
>
> +config HID_GT683R
> + tristate "LED support for the MSI GT683R"

How about rephrasing this description as "MSI GT683R LED support" as this
should make entry easier to find in Kconfig.

As there appears to be more models that could use this driver (and uses
the same PID) perhaps you should use "MSI GT68xR" or similar depending
on what models there are. We now of at least GX680R (having the same
PID, at least).

There's no need to rename the driver and every function or variable
below though (i.e. a gt683r prefix is still perfectly fine).

> + depends on LEDS_CLASS && USB_HID
> + ---help---
> + Say Y here if you want to enable support for the MSI GT683R LEDS
> +

checkpatch.pl suggests adding descriptive paragraph here.

> config HID_EMS_FF
> tristate "EMS Production Inc. force feedback support"
> depends on HID
> diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
> index fc712dd..111304e 100644
> --- a/drivers/hid/Makefile
> +++ b/drivers/hid/Makefile
> @@ -44,6 +44,7 @@ obj-$(CONFIG_HID_CHICONY) += hid-chicony.o
> obj-$(CONFIG_HID_CP2112) += hid-cp2112.o
> obj-$(CONFIG_HID_CYPRESS) += hid-cypress.o
> obj-$(CONFIG_HID_DRAGONRISE) += hid-dr.o
> +obj-$(CONFIG_HID_GT683R) += hid-gt683r.o

Keep the entries sorted?

> obj-$(CONFIG_HID_EMS_FF) += hid-emsff.o
> obj-$(CONFIG_HID_ELECOM) += hid-elecom.o
> obj-$(CONFIG_HID_ELO) += hid-elo.o
> diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
> index da52279..ec88fdb 100644
> --- a/drivers/hid/hid-core.c
> +++ b/drivers/hid/hid-core.c
> @@ -1827,6 +1827,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
> { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) },
> { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_OFFICE_KB) },
> { HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) },
> + { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
> { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) },
> { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_1) },
> { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_2) },
> diff --git a/drivers/hid/hid-gt683r.c b/drivers/hid/hid-gt683r.c
> new file mode 100644
> index 0000000..4baaa36
> --- /dev/null
> +++ b/drivers/hid/hid-gt683r.c
> @@ -0,0 +1,186 @@
> +/*
> + * MSI GT683R led driver
> + *
> + * Copyright (c) 2014 Janne Kanniainen <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/hid.h>
> +#include <linux/kernel.h>
> +#include <linux/leds.h>
> +#include <linux/module.h>
> +
> +#include "hid-ids.h"
> +
> +#define GT683R_LED_BACK BIT(0)
> +#define GT683R_LED_SIDE BIT(1)
> +#define GT683R_LED_FRONT BIT(2)
> +#define GT683R_LED_ALL (GT683R_LED_BACK | GT683R_LED_SIDE | GT683R_LED_FRONT)

Since it seems you are able to control these three LEDs independently,
shouldn't you consider registering three LEDs?

> +
> +#define GT683R_LED_OFF 0
> +#define GT683R_LED_AUDIO 2
> +#define GT683R_LED_BREATHING 3
> +#define GT683R_LED_NORMAL 5

What are AUDIO and BREATHING for? Perhaps add a descriptive comment?

> +
> +#define GT683R_BUFFER_SIZE 8
> +
> +struct gt683r_led {
> + struct hid_device *hdev;
> + struct led_classdev led_dev;
> + struct mutex lock;
> + struct work_struct work;
> + enum led_brightness brightness;
> +};
> +
> +static const struct hid_device_id gt683r_led_id[] = {
> + { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
> + { }
> +};
> +
> +static const char gt683r_led_select_leds[GT683R_BUFFER_SIZE] = { 0x01, 0x02, 0x30, 0x00,
> + 0x00, 0x00, 0x00, 0x00 };
> +static const char gt683r_led_select_type[GT683R_BUFFER_SIZE] = { 0x01, 0x02, 0x20, 0x00,
> + 0x01, 0x00, 0x00, 0x00 };

80 char limit.

Perhaps move these to gt683r_led_set, which is the only place where they
are used?

> +
> +static void gt683r_brightness_set(struct led_classdev *led_cdev,
> + enum led_brightness brightness)
> +{
> + struct gt683r_led *led =
> + container_of(led_cdev, struct gt683r_led, led_dev);
> +
> + led->brightness = brightness;
> +
> + schedule_work(&led->work);
> +}
> +
> +static int gt683r_led_snd_msg(struct gt683r_led *led, char *msg)
> +{
> + int ret;
> +
> + ret = hid_hw_raw_request(led->hdev, 0x01, msg, GT683R_BUFFER_SIZE,
> + HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
> + if (ret < 0) {
> + hid_err(led->hdev,
> + "failed to send set report request: %i\n", ret);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static void gt683r_led_set(struct gt683r_led *led, char type)
> +{
> + char *buffer;
> +
> + buffer = kmalloc(GT683R_BUFFER_SIZE, GFP_KERNEL);
> + if (!buffer) {
> + hid_err(led->hdev, "can\'t allocate buffer\n");

No need to log OOM messages as this would already have been taken care
of by the memory subsystem.

> + return;
> + }
> +
> + memcpy(buffer, gt683r_led_select_leds, GT683R_BUFFER_SIZE);
> + buffer[3] = GT683R_LED_ALL;
> + gt683r_led_snd_msg(led, buffer);

Error handling? Perhaps there's no point in sending the "type" report
if select_leds failed?

> +
> + memcpy(buffer, gt683r_led_select_type, GT683R_BUFFER_SIZE);
> + buffer[3] = type;
> + gt683r_led_snd_msg(led, buffer);

You don't have to change this if you don't want, but wouldn't
"set_state" be more descriptive than "select_type"?

> +
> + kfree(buffer);
> +}
> +
> +static void gt683r_led_work(struct work_struct *work)
> +{
> + struct gt683r_led *led =
> + container_of(work, struct gt683r_led, work);
> +
> + mutex_lock(&led->lock);
> +
> + if (led->brightness)
> + gt683r_led_set(led, GT683R_LED_NORMAL);
> + else
> + gt683r_led_set(led, GT683R_LED_OFF);
> +
> + mutex_unlock(&led->lock);
> +}
> +
> +static struct led_classdev gt683r_led_dev = {
> + .name = "gt683r-led",
> + .brightness_set = gt683r_brightness_set,
> + .max_brightness = 1,
> + .flags = LED_CORE_SUSPENDRESUME,
> +};
> +
> +static int gt683r_led_probe(struct hid_device *hdev,
> + const struct hid_device_id *id)
> +{
> + int ret;
> + struct gt683r_led *led;
> +
> + led = devm_kzalloc(&hdev->dev, sizeof(struct gt683r_led), GFP_KERNEL);
> + if (!led)
> + return -ENOMEM;
> +
> + led->hdev = hdev;
> + hid_set_drvdata(hdev, led);
> +
> + ret = hid_parse(hdev);
> + if (ret) {
> + hid_err(hdev, "hid parsing failed\n");
> + goto fail;
> + }
> +
> + ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
> + if (ret) {
> + hid_err(hdev, "hw start failed\n");
> + goto fail;
> + }
> +
> + led->led_dev = gt683r_led_dev;
> + ret = led_classdev_register(&hdev->dev, &led->led_dev);
> + if (ret) {
> + hid_err(hdev, "could not register led device\n");
> + goto stop;
> + }
> +
> + mutex_init(&led->lock);
> + INIT_WORK(&led->work, gt683r_led_work);
> +
> + return 0;
> +stop:
> + hid_hw_stop(hdev);
> +fail:
> + return ret;
> +}
> +
> +static void gt683r_led_remove(struct hid_device *hdev)
> +{
> + struct gt683r_led *led = hid_get_drvdata(hdev);
> +
> + led_classdev_unregister(&led->led_dev);
> + cancel_work_sync(&led->work);
> + hid_hw_stop(hdev);
> +}
> +
> +static struct hid_driver gt683r_led_driver = {
> + .probe = gt683r_led_probe,
> + .remove = gt683r_led_remove,
> + .name = "gt683r_led",
> + .id_table = gt683r_led_id,
> +};
> +
> +module_hid_driver(gt683r_led_driver);
> +
> +MODULE_AUTHOR("Janne Kanniainen");
> +MODULE_DESCRIPTION("MSI GT683R led driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
> index 34bb220..e2097f7 100644
> --- a/drivers/hid/hid-ids.h
> +++ b/drivers/hid/hid-ids.h
> @@ -641,6 +641,7 @@
> #define USB_DEVICE_ID_GENIUS_KB29E 0x3004
>
> #define USB_VENDOR_ID_MSI 0x1770
> +#define USB_DEVICE_ID_MSI_GT683R_LED_PANEL 0xff00
> #define USB_DEVICE_ID_MSI_GX680R_LED_PANEL 0xff00

As these device have the same PID you shouldn't be adding a duplicate
entry to the blacklist_table below. Perhaps you can rename the original
one as GC68XR instead (or just remove the old one).

>
> #define USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR 0x0400
> diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
> index 8e4ddb3..927e7be 100644
> --- a/drivers/hid/usbhid/hid-quirks.c
> +++ b/drivers/hid/usbhid/hid-quirks.c
> @@ -73,6 +73,7 @@ static const struct hid_blacklist {
> { USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS },
> { USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET },
> { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET },
> + { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
> { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GX680R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
> { USB_VENDOR_ID_NEXIO, USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750, HID_QUIRK_NO_INIT_REPORTS },
> { USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_MOUSE, HID_QUIRK_NO_INIT_REPORTS },

Thanks,
Johan

2014-06-11 14:07:31

by Johan Hovold

[permalink] [raw]
Subject: Re: [PATCH v3] leds: USB: HID: Add support for MSI GT683R led panels

On Wed, Jun 11, 2014 at 01:25:49PM +0200, Jiri Kosina wrote:
> On Wed, 11 Jun 2014, Janne Kanniainen wrote:
>
> > This driver adds support for USB controlled led panels that exists in MSI GT683R laptop
> >
> > Changes in v2:
> > - sorted headers to alphabetic order
> > - using devm_kzalloc
> > - using BIT(n)
> > - using usb_control_msg instead of usb_submit_urb
> > - removing unneeded code
> >
> > Changes in v3:
> > - implemented as HID device
> > - some cleanups and bug fixes
> >
> > Signed-off-by: Janne Kanniainen <[email protected]>
>
> Thanks for the driver. Johan, as you did a very good job reviewing this
> before I managed to get to it, I'd be glad to put your Reviewed-by: to the
> commit before commiting it, if you are going to provide it.

I had a few more comments to v3, but I'll make sure to reply with a
Reviewed-by-tag before you apply.

Thanks,
Johan

2014-06-11 15:30:50

by Johan Hovold

[permalink] [raw]
Subject: Re: [PATCH v3] leds: USB: HID: Add support for MSI GT683R led panels

On Wed, Jun 11, 2014 at 04:05:42PM +0200, Johan Hovold wrote:
> On Wed, Jun 11, 2014 at 12:21:39AM +0300, Janne Kanniainen wrote:

> > +static const char gt683r_led_select_leds[GT683R_BUFFER_SIZE] = { 0x01, 0x02, 0x30, 0x00,
> > + 0x00, 0x00, 0x00, 0x00 };
> > +static const char gt683r_led_select_type[GT683R_BUFFER_SIZE] = { 0x01, 0x02, 0x20, 0x00,
> > + 0x01, 0x00, 0x00, 0x00 };
>
> 80 char limit.
>
> Perhaps move these to gt683r_led_set, which is the only place where they
> are used?

Or, as I hinted earlier, just allocate the 8-byte buffer using kzalloc
and only initialise the non-zero bytes directly. The first byte should
be the report id (0x01). I noticed that some hid-drivers use this fact
when sending the raw request (see below).

> > +
> > +static void gt683r_brightness_set(struct led_classdev *led_cdev,
> > + enum led_brightness brightness)
> > +{
> > + struct gt683r_led *led =
> > + container_of(led_cdev, struct gt683r_led, led_dev);
> > +
> > + led->brightness = brightness;
> > +
> > + schedule_work(&led->work);
> > +}
> > +
> > +static int gt683r_led_snd_msg(struct gt683r_led *led, char *msg)
> > +{
> > + int ret;
> > +
> > + ret = hid_hw_raw_request(led->hdev, 0x01, msg, GT683R_BUFFER_SIZE,
> > + HID_FEATURE_REPORT, HID_REQ_SET_REPORT);

That is, you could use msg[0] here instead of 0x01.

> > + if (ret < 0) {
> > + hid_err(led->hdev,
> > + "failed to send set report request: %i\n", ret);
> > + return ret;
> > + }
> > +
> > + return 0;
> > +}

Johan

2014-06-11 17:34:59

by Johan Hovold

[permalink] [raw]
Subject: Re: [PATCH v3] leds: USB: HID: Add support for MSI GT683R led panels

On Wed, Jun 11, 2014 at 04:05:42PM +0200, Johan Hovold wrote:
>
On Wed, Jun 11, 2014 at 12:21:39AM +0300, Janne Kanniainen wrote:
> > +static int gt683r_led_snd_msg(struct gt683r_led *led, char *msg)
> > +{
> > + int ret;
> > +
> > + ret = hid_hw_raw_request(led->hdev, 0x01, msg, GT683R_BUFFER_SIZE,
> > + HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
> > + if (ret < 0) {
> > + hid_err(led->hdev,
> > + "failed to send set report request: %i\n", ret);

And here's one more: You need to check if ret != GT683R_BUFFER_SIZE and
make sure to return an error (e.g. -EIO) even if ret >= 0 in that case.

> > + return ret;
> > + }
> > +
> > + return 0;
> > +}

Johan

2014-06-11 22:49:23

by Janne Kanniainen

[permalink] [raw]
Subject: [PATCH v4] leds: USB: HID: Add support for MSI GT683R led panels

This driver adds support for USB controlled led panels that exists in MSI GT683R laptop

Signed-off-by: Janne Kanniainen <[email protected]>
---
Changes in v2:
- sorted headers to alphabetic order
- using devm_kzalloc
- using BIT(n)
- using usb_control_msg instead of usb_submit_urb
- removing unneeded code

Changes in v3:
- implemented as HID device
- some cleanups and bug fixes

Changes in v4:
- more cleanups
- support for selecting leds
- support for selecting status

drivers/hid/Kconfig | 11 ++
drivers/hid/Makefile | 1 +
drivers/hid/hid-core.c | 1 +
drivers/hid/hid-gt683r.c | 320 ++++++++++++++++++++++++++++++++++++++++
drivers/hid/hid-ids.h | 2 +-
drivers/hid/usbhid/hid-quirks.c | 2 +-
6 files changed, 335 insertions(+), 2 deletions(-)
create mode 100644 drivers/hid/hid-gt683r.c

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 7af9d0b..d93e0ae 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -261,6 +261,17 @@ config HOLTEK_FF
Say Y here if you have a Holtek On Line Grip based game controller
and want to have force feedback support for it.

+config HID_GT683R
+ tristate "MSI GT68xR LED support"
+ depends on LEDS_CLASS && USB_HID
+ ---help---
+ Say Y here if you want to enable support for the MSI GT68xR LEDS
+
+ This driver support following states normal, breathing and audio.
+ You can also select which leds you want to enable.
+ Currently the following devices are know to be supported:
+ - MSI GT683R
+
config HID_HUION
tristate "Huion tablets"
depends on USB_HID
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index fc712dd..7129311 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -48,6 +48,7 @@ obj-$(CONFIG_HID_EMS_FF) += hid-emsff.o
obj-$(CONFIG_HID_ELECOM) += hid-elecom.o
obj-$(CONFIG_HID_ELO) += hid-elo.o
obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o
+obj-$(CONFIG_HID_GT683R) += hid-gt683r.o
obj-$(CONFIG_HID_GYRATION) += hid-gyration.o
obj-$(CONFIG_HID_HOLTEK) += hid-holtek-kbd.o
obj-$(CONFIG_HID_HOLTEK) += hid-holtek-mouse.o
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index da52279..ec88fdb 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1827,6 +1827,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_OFFICE_KB) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) },
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_1) },
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_2) },
diff --git a/drivers/hid/hid-gt683r.c b/drivers/hid/hid-gt683r.c
new file mode 100644
index 0000000..04e4cc2
--- /dev/null
+++ b/drivers/hid/hid-gt683r.c
@@ -0,0 +1,320 @@
+/*
+ * MSI GT683R led driver
+ *
+ * Copyright (c) 2014 Janne Kanniainen <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+#define GT683R_LED_BACK BIT(0)
+#define GT683R_LED_SIDE BIT(1)
+#define GT683R_LED_FRONT BIT(2)
+
+#define GT683R_BUFFER_SIZE 8
+
+/*
+ * GT683R_LED_OFF: all LEDs are off
+ * GT683R_LED_AUDIO: the status of LEDs depends
+ * on sound level
+ * GT683R_LED_BREATHING: LEDs brightness varies
+ * at human breathing rate
+ * GT683R_LED_NORMAL: LEDs are on
+ */
+enum gt683r_led_state {
+ GT683R_LED_OFF = 0,
+ GT683R_LED_AUDIO = 2,
+ GT683R_LED_BREATHING = 3,
+ GT683R_LED_NORMAL = 5
+};
+
+struct gt683r_led {
+ struct hid_device *hdev;
+ struct led_classdev led_dev_back;
+ struct led_classdev led_dev_side;
+ struct led_classdev led_dev_front;
+ struct mutex lock;
+ struct mutex state_lock;
+ struct work_struct work;
+ enum led_brightness brightness_back;
+ enum led_brightness brightness_side;
+ enum led_brightness brightness_front;
+ enum gt683r_led_state state;
+};
+
+static const struct hid_device_id gt683r_led_id[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
+ { }
+};
+
+#define GT683R_BRIGHTNESS_SET(name) \
+static void gt683r_brightness_set_##name(struct led_classdev *led_cdev, \
+ enum led_brightness brightness) \
+{ \
+ struct gt683r_led *led = \
+ container_of(led_cdev, struct gt683r_led, \
+ led_dev_##name); \
+ \
+ led->brightness_##name = brightness; \
+ \
+ schedule_work(&led->work); \
+}
+
+GT683R_BRIGHTNESS_SET(back);
+GT683R_BRIGHTNESS_SET(side);
+GT683R_BRIGHTNESS_SET(front);
+
+static ssize_t gt683r_show_state(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct hid_device *hdev =
+ container_of(dev, struct hid_device, dev);
+ struct gt683r_led *led = hid_get_drvdata(hdev);
+
+ if (led->state == GT683R_LED_OFF)
+ return sprintf(buf, "off\n");
+ else if (led->state == GT683R_LED_AUDIO)
+ return sprintf(buf, "audio\n");
+ else if (led->state == GT683R_LED_BREATHING)
+ return sprintf(buf, "breathing\n");
+ else
+ return sprintf(buf, "normal\n");
+}
+
+static ssize_t gt683r_store_state(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct hid_device *hdev =
+ container_of(dev, struct hid_device, dev);
+ struct gt683r_led *led = hid_get_drvdata(hdev);
+
+ mutex_lock(&led->state_lock);
+ if (!strncmp("off", buf, strlen("off"))) {
+ led->state = GT683R_LED_OFF;
+ } else if (!strncmp("audio", buf, strlen("audio"))) {
+ led->state = GT683R_LED_AUDIO;
+ } else if (!strncmp("breathing", buf, strlen("breathing"))) {
+ led->state = GT683R_LED_BREATHING;
+ } else if (!strncmp("normal", buf, strlen("normal"))) {
+ led->state = GT683R_LED_NORMAL;
+ } else {
+ count = -EINVAL;
+ goto fail;
+ }
+
+ schedule_work(&led->work);
+
+fail:
+ mutex_unlock(&led->state_lock);
+
+ return count;
+}
+
+static int gt683r_led_snd_msg(struct gt683r_led *led, char *msg)
+{
+ int ret;
+
+ ret = hid_hw_raw_request(led->hdev, msg[0], msg, GT683R_BUFFER_SIZE,
+ HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
+ if (ret != GT683R_BUFFER_SIZE) {
+ hid_err(led->hdev,
+ "failed to send set report request: %i\n", ret);
+ return ret < 0 ? ret : -EIO;
+ }
+
+ return 0;
+}
+
+static void gt683r_led_set(struct gt683r_led *led, char leds, char state)
+{
+ char *buffer;
+
+ buffer = kzalloc(GT683R_BUFFER_SIZE, GFP_KERNEL);
+ if (!buffer)
+ return;
+
+ buffer[0] = 0x01;
+ buffer[1] = 0x02;
+ buffer[2] = 0x30;
+ buffer[3] = leds;
+ if (gt683r_led_snd_msg(led, buffer))
+ goto fail;
+
+ buffer[2] = 0x20;
+ buffer[3] = state;
+ buffer[4] = 0x01;
+ gt683r_led_snd_msg(led, buffer);
+
+fail:
+ kfree(buffer);
+}
+
+static void gt683r_led_work(struct work_struct *work)
+{
+ struct gt683r_led *led =
+ container_of(work, struct gt683r_led, work);
+ char leds = 0;
+
+ mutex_lock(&led->lock);
+
+ if (led->brightness_back)
+ leds |= GT683R_LED_BACK;
+
+ if (led->brightness_side)
+ leds |= GT683R_LED_SIDE;
+
+ if (led->brightness_front)
+ leds |= GT683R_LED_FRONT;
+
+ if (leds)
+ gt683r_led_set(led, leds, led->state);
+ else
+ gt683r_led_set(led, leds, GT683R_LED_OFF);
+
+ mutex_unlock(&led->lock);
+}
+
+static struct led_classdev gt683r_led_dev_back = {
+ .name = "gt683r-led:back",
+ .brightness_set = gt683r_brightness_set_back,
+ .max_brightness = 1,
+ .flags = LED_CORE_SUSPENDRESUME,
+};
+
+static struct led_classdev gt683r_led_dev_side = {
+ .name = "gt683r-led:side",
+ .brightness_set = gt683r_brightness_set_side,
+ .max_brightness = 1,
+ .flags = LED_CORE_SUSPENDRESUME,
+};
+
+static struct led_classdev gt683r_led_dev_front = {
+ .name = "gt683r-led:front",
+ .brightness_set = gt683r_brightness_set_front,
+ .max_brightness = 1,
+ .flags = LED_CORE_SUSPENDRESUME,
+};
+
+static struct device_attribute gt683r_led_state_attribute = {
+ .attr = {
+ .name = "state",
+ .mode = 0644,
+ },
+ .show = gt683r_show_state,
+ .store = gt683r_store_state,
+};
+
+static int gt683r_led_probe(struct hid_device *hdev,
+ const struct hid_device_id *id)
+{
+ int ret;
+ struct gt683r_led *led;
+
+ led = devm_kzalloc(&hdev->dev, sizeof(struct gt683r_led), GFP_KERNEL);
+ if (!led)
+ return -ENOMEM;
+
+ led->hdev = hdev;
+ hid_set_drvdata(hdev, led);
+
+ ret = hid_parse(hdev);
+ if (ret) {
+ hid_err(hdev, "hid parsing failed\n");
+ goto fail;
+ }
+
+ ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
+ if (ret) {
+ hid_err(hdev, "hw start failed\n");
+ goto fail;
+ }
+
+ led->led_dev_back = gt683r_led_dev_back;
+ ret = led_classdev_register(&hdev->dev, &led->led_dev_back);
+ if (ret) {
+ hid_err(hdev, "could not register led device\n");
+ goto fail_back;
+ }
+
+ led->led_dev_side = gt683r_led_dev_side;
+ ret = led_classdev_register(&hdev->dev, &led->led_dev_side);
+ if (ret) {
+ hid_err(hdev, "could not register led device\n");
+ goto fail_side;
+ }
+
+ led->led_dev_front = gt683r_led_dev_front;
+ ret = led_classdev_register(&hdev->dev, &led->led_dev_front);
+ if (ret) {
+ hid_err(hdev, "could not register led device\n");
+ goto fail_front;
+ }
+
+ ret = device_create_file(&led->hdev->dev,
+ &gt683r_led_state_attribute);
+ if (ret) {
+ hid_err(hdev, "could not make state attribute file\n");
+ goto fail_create_file;
+ }
+
+ mutex_init(&led->lock);
+ mutex_init(&led->state_lock);
+ INIT_WORK(&led->work, gt683r_led_work);
+
+ return 0;
+
+fail_create_file:
+ led_classdev_unregister(&led->led_dev_front);
+fail_front:
+ led_classdev_unregister(&led->led_dev_side);
+fail_side:
+ led_classdev_unregister(&led->led_dev_back);
+fail_back:
+ hid_hw_stop(hdev);
+fail:
+ return ret;
+}
+
+static void gt683r_led_remove(struct hid_device *hdev)
+{
+ struct gt683r_led *led = hid_get_drvdata(hdev);
+
+ led_classdev_unregister(&led->led_dev_side);
+ led_classdev_unregister(&led->led_dev_back);
+ led_classdev_unregister(&led->led_dev_front);
+ cancel_work_sync(&led->work);
+ device_remove_file(&hdev->dev,
+ &gt683r_led_state_attribute);
+ hid_hw_stop(hdev);
+}
+
+static struct hid_driver gt683r_led_driver = {
+ .probe = gt683r_led_probe,
+ .remove = gt683r_led_remove,
+ .name = "gt683r_led",
+ .id_table = gt683r_led_id,
+};
+
+module_hid_driver(gt683r_led_driver);
+
+MODULE_AUTHOR("Janne Kanniainen");
+MODULE_DESCRIPTION("MSI GT683R led driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 34bb220..3692d37 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -641,7 +641,7 @@
#define USB_DEVICE_ID_GENIUS_KB29E 0x3004

#define USB_VENDOR_ID_MSI 0x1770
-#define USB_DEVICE_ID_MSI_GX680R_LED_PANEL 0xff00
+#define USB_DEVICE_ID_MSI_GT683R_LED_PANEL 0xff00

#define USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR 0x0400
#define USB_DEVICE_ID_N_S_HARMONY 0xc359
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
index 8e4ddb3..c640e1d 100644
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -73,7 +73,7 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET },
- { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GX680R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
+ { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_NEXIO, USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_MOUSE, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NO_INIT_REPORTS },
--
1.9.2

2014-06-12 09:07:26

by Johan Hovold

[permalink] [raw]
Subject: Re: [PATCH v4] leds: USB: HID: Add support for MSI GT683R led panels

On Thu, Jun 12, 2014 at 01:48:41AM +0300, Janne Kanniainen wrote:
> This driver adds support for USB controlled led panels that exists in MSI GT683R laptop

You forgot to break this line.

>
> Signed-off-by: Janne Kanniainen <[email protected]>
> ---
> Changes in v2:
> - sorted headers to alphabetic order
> - using devm_kzalloc
> - using BIT(n)
> - using usb_control_msg instead of usb_submit_urb
> - removing unneeded code
>
> Changes in v3:
> - implemented as HID device
> - some cleanups and bug fixes
>
> Changes in v4:
> - more cleanups
> - support for selecting leds
> - support for selecting status

That was fast. :)

>
> drivers/hid/Kconfig | 11 ++
> drivers/hid/Makefile | 1 +
> drivers/hid/hid-core.c | 1 +
> drivers/hid/hid-gt683r.c | 320 ++++++++++++++++++++++++++++++++++++++++
> drivers/hid/hid-ids.h | 2 +-
> drivers/hid/usbhid/hid-quirks.c | 2 +-
> 6 files changed, 335 insertions(+), 2 deletions(-)
> create mode 100644 drivers/hid/hid-gt683r.c
>
> diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
> index 7af9d0b..d93e0ae 100644
> --- a/drivers/hid/Kconfig
> +++ b/drivers/hid/Kconfig
> @@ -261,6 +261,17 @@ config HOLTEK_FF
> Say Y here if you have a Holtek On Line Grip based game controller
> and want to have force feedback support for it.
>
> +config HID_GT683R
> + tristate "MSI GT68xR LED support"
> + depends on LEDS_CLASS && USB_HID
> + ---help---
> + Say Y here if you want to enable support for the MSI GT68xR LEDS
> +
> + This driver support following states normal, breathing and audio.
> + You can also select which leds you want to enable.
> + Currently the following devices are know to be supported:
> + - MSI GT683R
> +
> config HID_HUION
> tristate "Huion tablets"
> depends on USB_HID
> diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
> index fc712dd..7129311 100644
> --- a/drivers/hid/Makefile
> +++ b/drivers/hid/Makefile
> @@ -48,6 +48,7 @@ obj-$(CONFIG_HID_EMS_FF) += hid-emsff.o
> obj-$(CONFIG_HID_ELECOM) += hid-elecom.o
> obj-$(CONFIG_HID_ELO) += hid-elo.o
> obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o
> +obj-$(CONFIG_HID_GT683R) += hid-gt683r.o
> obj-$(CONFIG_HID_GYRATION) += hid-gyration.o
> obj-$(CONFIG_HID_HOLTEK) += hid-holtek-kbd.o
> obj-$(CONFIG_HID_HOLTEK) += hid-holtek-mouse.o
> diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
> index da52279..ec88fdb 100644
> --- a/drivers/hid/hid-core.c
> +++ b/drivers/hid/hid-core.c
> @@ -1827,6 +1827,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
> { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) },
> { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_OFFICE_KB) },
> { HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) },
> + { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
> { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) },
> { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_1) },
> { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_2) },
> diff --git a/drivers/hid/hid-gt683r.c b/drivers/hid/hid-gt683r.c
> new file mode 100644
> index 0000000..04e4cc2
> --- /dev/null
> +++ b/drivers/hid/hid-gt683r.c
> @@ -0,0 +1,320 @@
> +/*
> + * MSI GT683R led driver
> + *
> + * Copyright (c) 2014 Janne Kanniainen <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/device.h>
> +#include <linux/hid.h>
> +#include <linux/kernel.h>
> +#include <linux/leds.h>
> +#include <linux/module.h>
> +
> +#include "hid-ids.h"
> +
> +#define GT683R_LED_BACK BIT(0)
> +#define GT683R_LED_SIDE BIT(1)
> +#define GT683R_LED_FRONT BIT(2)
> +
> +#define GT683R_BUFFER_SIZE 8
> +
> +/*
> + * GT683R_LED_OFF: all LEDs are off
> + * GT683R_LED_AUDIO: the status of LEDs depends
> + * on sound level
> + * GT683R_LED_BREATHING: LEDs brightness varies
> + * at human breathing rate
> + * GT683R_LED_NORMAL: LEDs are on
> + */
> +enum gt683r_led_state {
> + GT683R_LED_OFF = 0,
> + GT683R_LED_AUDIO = 2,
> + GT683R_LED_BREATHING = 3,
> + GT683R_LED_NORMAL = 5
> +};
> +
> +struct gt683r_led {
> + struct hid_device *hdev;
> + struct led_classdev led_dev_back;
> + struct led_classdev led_dev_side;
> + struct led_classdev led_dev_front;

You could store these as an array, and add an enum for back, side, and
front (more below).

> + struct mutex lock;
> + struct mutex state_lock;

You should be able to use only one lock.

> + struct work_struct work;
> + enum led_brightness brightness_back;
> + enum led_brightness brightness_side;
> + enum led_brightness brightness_front;

These could then also be in an array.

> + enum gt683r_led_state state;
> +};
> +
> +static const struct hid_device_id gt683r_led_id[] = {
> + { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
> + { }
> +};
> +
> +#define GT683R_BRIGHTNESS_SET(name) \
> +static void gt683r_brightness_set_##name(struct led_classdev *led_cdev, \
> + enum led_brightness brightness) \
> +{ \
> + struct gt683r_led *led = \
> + container_of(led_cdev, struct gt683r_led, \
> + led_dev_##name); \
> + \
> + led->brightness_##name = brightness; \
> + \
> + schedule_work(&led->work); \
> +}
> +
> +GT683R_BRIGHTNESS_SET(back);
> +GT683R_BRIGHTNESS_SET(side);
> +GT683R_BRIGHTNESS_SET(front);

This is one way of solving this, but it's better to have one
set_brightness function and determine which led it is called for by
accessing the driver data of the hid device (via the parent device) and
iterate over the available leds. See drivers/hid/hid-lg4ff.c for an
example of how this can be implemented.

> +
> +static ssize_t gt683r_show_state(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> +{
> + struct hid_device *hdev =
> + container_of(dev, struct hid_device, dev);
> + struct gt683r_led *led = hid_get_drvdata(hdev);
> +
> + if (led->state == GT683R_LED_OFF)
> + return sprintf(buf, "off\n");

You should always use scnprintf (with a size of PAGE_SIZE) in show
callbacks.

> + else if (led->state == GT683R_LED_AUDIO)
> + return sprintf(buf, "audio\n");
> + else if (led->state == GT683R_LED_BREATHING)
> + return sprintf(buf, "breathing\n");
> + else
> + return sprintf(buf, "normal\n");
> +}

Ok, so the blink mode (or type rather than state, I understand now --
sorry for the confusion) is common for all three LEDs.

You shouldn't allow the LEDs to be disabled through this attribute, but
rather set the mode that will be used for any enabled LEDs (e.g. the
attribute should only have three possible values).

Would it even be sufficient to only set the blink mode (type, state) to
NORMAL at probe and then only update it if it changes (in store_state
below)? In particular, will all three LEDs stay off if they are masked
out in gt683r_let_set? (Otherwise, you could only set OFF mode when all
three LEDs are disabled.)

Note that any new attribute has to be documented under
Documentation/ABI.

> +
> +static ssize_t gt683r_store_state(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + struct hid_device *hdev =
> + container_of(dev, struct hid_device, dev);
> + struct gt683r_led *led = hid_get_drvdata(hdev);
> +
> + mutex_lock(&led->state_lock);
> + if (!strncmp("off", buf, strlen("off"))) {
> + led->state = GT683R_LED_OFF;
> + } else if (!strncmp("audio", buf, strlen("audio"))) {
> + led->state = GT683R_LED_AUDIO;
> + } else if (!strncmp("breathing", buf, strlen("breathing"))) {
> + led->state = GT683R_LED_BREATHING;
> + } else if (!strncmp("normal", buf, strlen("normal"))) {
> + led->state = GT683R_LED_NORMAL;
> + } else {
> + count = -EINVAL;
> + goto fail;
> + }
> +
> + schedule_work(&led->work);
> +
> +fail:
> + mutex_unlock(&led->state_lock);
> +
> + return count;
> +}

I think you should use an integer value (and snprintf) for the three
modes (e.g. 0: normal, 1: audio, 2: breathing) and just document that
in the ABI files mentioned above.

> +
> +static int gt683r_led_snd_msg(struct gt683r_led *led, char *msg)

I didn't notice before, but you should be using the unsigned u8 type for
msg (and leds and state below).

> +{
> + int ret;
> +
> + ret = hid_hw_raw_request(led->hdev, msg[0], msg, GT683R_BUFFER_SIZE,
> + HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
> + if (ret != GT683R_BUFFER_SIZE) {
> + hid_err(led->hdev,
> + "failed to send set report request: %i\n", ret);
> + return ret < 0 ? ret : -EIO;

I try to avoid ?: constructs.

> + }
> +
> + return 0;
> +}
> +
> +static void gt683r_led_set(struct gt683r_led *led, char leds, char state)
> +{
> + char *buffer;
> +
> + buffer = kzalloc(GT683R_BUFFER_SIZE, GFP_KERNEL);
> + if (!buffer)
> + return;
> +
> + buffer[0] = 0x01;
> + buffer[1] = 0x02;
> + buffer[2] = 0x30;
> + buffer[3] = leds;
> + if (gt683r_led_snd_msg(led, buffer))
> + goto fail;
> +
> + buffer[2] = 0x20;
> + buffer[3] = state;
> + buffer[4] = 0x01;
> + gt683r_led_snd_msg(led, buffer);
> +
> +fail:
> + kfree(buffer);
> +}
> +
> +static void gt683r_led_work(struct work_struct *work)
> +{
> + struct gt683r_led *led =
> + container_of(work, struct gt683r_led, work);
> + char leds = 0;
> +
> + mutex_lock(&led->lock);
> +
> + if (led->brightness_back)
> + leds |= GT683R_LED_BACK;
> +
> + if (led->brightness_side)
> + leds |= GT683R_LED_SIDE;
> +
> + if (led->brightness_front)
> + leds |= GT683R_LED_FRONT;
> +
> + if (leds)
> + gt683r_led_set(led, leds, led->state);
> + else
> + gt683r_led_set(led, leds, GT683R_LED_OFF);
> +
> + mutex_unlock(&led->lock);
> +}
> +
> +static struct led_classdev gt683r_led_dev_back = {
> + .name = "gt683r-led:back",
> + .brightness_set = gt683r_brightness_set_back,
> + .max_brightness = 1,
> + .flags = LED_CORE_SUSPENDRESUME,
> +};
> +
> +static struct led_classdev gt683r_led_dev_side = {
> + .name = "gt683r-led:side",
> + .brightness_set = gt683r_brightness_set_side,
> + .max_brightness = 1,
> + .flags = LED_CORE_SUSPENDRESUME,
> +};
> +
> +static struct led_classdev gt683r_led_dev_front = {
> + .name = "gt683r-led:front",
> + .brightness_set = gt683r_brightness_set_front,
> + .max_brightness = 1,
> + .flags = LED_CORE_SUSPENDRESUME,
> +};

You should remove these three as you only use them for initialising the
dynamically allocated instances, which could be initialised directly in
probe instead.

You can find examples of this under drivers/hid (e.g.
drivers/hid/hid-lg4ff.c) including how to initialise the name field.
Note that the recommended naming scheme is "devicename:colour:function",
but you can leave the colour out (but still keep the two ':').

> +
> +static struct device_attribute gt683r_led_state_attribute = {
> + .attr = {
> + .name = "state",
> + .mode = 0644,
> + },
> + .show = gt683r_show_state,
> + .store = gt683r_store_state,
> +};

You should use the DEVICE_ATTR macro instead.

> +
> +static int gt683r_led_probe(struct hid_device *hdev,
> + const struct hid_device_id *id)
> +{
> + int ret;
> + struct gt683r_led *led;
> +
> + led = devm_kzalloc(&hdev->dev, sizeof(struct gt683r_led), GFP_KERNEL);
> + if (!led)
> + return -ENOMEM;
> +
> + led->hdev = hdev;
> + hid_set_drvdata(hdev, led);
> +
> + ret = hid_parse(hdev);
> + if (ret) {
> + hid_err(hdev, "hid parsing failed\n");
> + goto fail;
> + }
> +
> + ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
> + if (ret) {
> + hid_err(hdev, "hw start failed\n");
> + goto fail;
> + }
> +
> + led->led_dev_back = gt683r_led_dev_back;
> + ret = led_classdev_register(&hdev->dev, &led->led_dev_back);
> + if (ret) {
> + hid_err(hdev, "could not register led device\n");
> + goto fail_back;
> + }
> +
> + led->led_dev_side = gt683r_led_dev_side;
> + ret = led_classdev_register(&hdev->dev, &led->led_dev_side);
> + if (ret) {
> + hid_err(hdev, "could not register led device\n");
> + goto fail_side;
> + }
> +
> + led->led_dev_front = gt683r_led_dev_front;
> + ret = led_classdev_register(&hdev->dev, &led->led_dev_front);
> + if (ret) {
> + hid_err(hdev, "could not register led device\n");
> + goto fail_front;
> + }

With the enum an arrays mentioned above this could be implemented as a
loop.

> +
> + ret = device_create_file(&led->hdev->dev,
> + &gt683r_led_state_attribute);
> + if (ret) {
> + hid_err(hdev, "could not make state attribute file\n");
> + goto fail_create_file;
> + }
> +
> + mutex_init(&led->lock);
> + mutex_init(&led->state_lock);
> + INIT_WORK(&led->work, gt683r_led_work);
> +
> + return 0;
> +
> +fail_create_file:
> + led_classdev_unregister(&led->led_dev_front);
> +fail_front:
> + led_classdev_unregister(&led->led_dev_side);
> +fail_side:
> + led_classdev_unregister(&led->led_dev_back);

...with a roll-back loop here.

> +fail_back:
> + hid_hw_stop(hdev);
> +fail:
> + return ret;
> +}
> +
> +static void gt683r_led_remove(struct hid_device *hdev)
> +{
> + struct gt683r_led *led = hid_get_drvdata(hdev);
> +
> + led_classdev_unregister(&led->led_dev_side);
> + led_classdev_unregister(&led->led_dev_back);
> + led_classdev_unregister(&led->led_dev_front);
> + cancel_work_sync(&led->work);
> + device_remove_file(&hdev->dev,
> + &gt683r_led_state_attribute);
> + hid_hw_stop(hdev);
> +}
> +
> +static struct hid_driver gt683r_led_driver = {
> + .probe = gt683r_led_probe,
> + .remove = gt683r_led_remove,
> + .name = "gt683r_led",
> + .id_table = gt683r_led_id,
> +};
> +
> +module_hid_driver(gt683r_led_driver);
> +
> +MODULE_AUTHOR("Janne Kanniainen");
> +MODULE_DESCRIPTION("MSI GT683R led driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
> index 34bb220..3692d37 100644
> --- a/drivers/hid/hid-ids.h
> +++ b/drivers/hid/hid-ids.h
> @@ -641,7 +641,7 @@
> #define USB_DEVICE_ID_GENIUS_KB29E 0x3004
>
> #define USB_VENDOR_ID_MSI 0x1770
> -#define USB_DEVICE_ID_MSI_GX680R_LED_PANEL 0xff00
> +#define USB_DEVICE_ID_MSI_GT683R_LED_PANEL 0xff00
>
> #define USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR 0x0400
> #define USB_DEVICE_ID_N_S_HARMONY 0xc359
> diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
> index 8e4ddb3..c640e1d 100644
> --- a/drivers/hid/usbhid/hid-quirks.c
> +++ b/drivers/hid/usbhid/hid-quirks.c
> @@ -73,7 +73,7 @@ static const struct hid_blacklist {
> { USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS },
> { USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET },
> { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET },
> - { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GX680R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
> + { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
> { USB_VENDOR_ID_NEXIO, USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750, HID_QUIRK_NO_INIT_REPORTS },
> { USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_MOUSE, HID_QUIRK_NO_INIT_REPORTS },
> { USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NO_INIT_REPORTS },

Thanks,
Johan

2014-06-12 20:34:43

by Janne Kanniainen

[permalink] [raw]
Subject: [PATCH v5] leds: USB: HID: Add support for MSI GT683R led panels

This driver adds support for USB controlled led panels that exists in
MSI GT683R laptop

Signed-off-by: Janne Kanniainen <[email protected]>
---
Changes in v2:
- sorted headers to alphabetic order
- using devm_kzalloc
- using BIT(n)
- using usb_control_msg instead of usb_submit_urb
- removing unneeded code

Changes in v3:
- implemented as HID device
- some cleanups and bug fixes

Changes in v4:
- more cleanups
- support for selecting leds
- suppport for selecting status

Changes in v5:
- mode attribute documented under Documentation/ABI
- made array for led_classdev
- led devices uses now recommended naming scheme

.../ABI/testing/sysfs-class-hid-driver-gt683r | 10 +
drivers/hid/Kconfig | 11 +
drivers/hid/Makefile | 1 +
drivers/hid/hid-core.c | 1 +
drivers/hid/hid-gt683r.c | 294 +++++++++++++++++++++
drivers/hid/hid-ids.h | 2 +-
drivers/hid/usbhid/hid-quirks.c | 2 +-
7 files changed, 319 insertions(+), 2 deletions(-)
create mode 100644 Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
create mode 100644 drivers/hid/hid-gt683r.c

diff --git a/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
new file mode 100644
index 0000000..c4d604e
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
@@ -0,0 +1,10 @@
+What: /sys/class/hidraw/<hidraw>/device/state
+Date: Jun 2014
+KernelVersion: 3.15
+Contact: Janne Kanniainen <[email protected]>
+Description:
+ Set the mode of LEDs
+
+ 0 - normal
+ 1 - audio
+ 2 - breathing
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 7af9d0b..d93e0ae 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -261,6 +261,17 @@ config HOLTEK_FF
Say Y here if you have a Holtek On Line Grip based game controller
and want to have force feedback support for it.

+config HID_GT683R
+ tristate "MSI GT68xR LED support"
+ depends on LEDS_CLASS && USB_HID
+ ---help---
+ Say Y here if you want to enable support for the MSI GT68xR LEDS
+
+ This driver support following states normal, breathing and audio.
+ You can also select which leds you want to enable.
+ Currently the following devices are know to be supported:
+ - MSI GT683R
+
config HID_HUION
tristate "Huion tablets"
depends on USB_HID
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index fc712dd..7129311 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -48,6 +48,7 @@ obj-$(CONFIG_HID_EMS_FF) += hid-emsff.o
obj-$(CONFIG_HID_ELECOM) += hid-elecom.o
obj-$(CONFIG_HID_ELO) += hid-elo.o
obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o
+obj-$(CONFIG_HID_GT683R) += hid-gt683r.o
obj-$(CONFIG_HID_GYRATION) += hid-gyration.o
obj-$(CONFIG_HID_HOLTEK) += hid-holtek-kbd.o
obj-$(CONFIG_HID_HOLTEK) += hid-holtek-mouse.o
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index da52279..ec88fdb 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1827,6 +1827,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_OFFICE_KB) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) },
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_1) },
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_2) },
diff --git a/drivers/hid/hid-gt683r.c b/drivers/hid/hid-gt683r.c
new file mode 100644
index 0000000..6dffb76
--- /dev/null
+++ b/drivers/hid/hid-gt683r.c
@@ -0,0 +1,294 @@
+/*
+ * MSI GT683R led driver
+ *
+ * Copyright (c) 2014 Janne Kanniainen <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+#define GT683R_LED_BACK BIT(0)
+#define GT683R_LED_SIDE BIT(1)
+#define GT683R_LED_FRONT BIT(2)
+
+#define GT683R_BUFFER_SIZE 8
+
+/*
+ * GT683R_LED_OFF: all LEDs are off
+ * GT683R_LED_AUDIO: the status of LEDs depends
+ * on sound level
+ * GT683R_LED_BREATHING: LEDs brightness varies
+ * at human breathing rate
+ * GT683R_LED_NORMAL: LEDs are on
+ */
+enum gt683r_led_mode {
+ GT683R_LED_OFF = 0,
+ GT683R_LED_AUDIO = 2,
+ GT683R_LED_BREATHING = 3,
+ GT683R_LED_NORMAL = 5
+};
+
+enum gt683r_panels {
+ back,
+ side,
+ front,
+};
+
+const char *gt683r_panel_names[] = {
+ "gt683r::back",
+ "gt683r::side",
+ "gt683r::front",
+};
+
+struct gt683r_led {
+ struct hid_device *hdev;
+ struct led_classdev *led_devs[3];
+ struct mutex lock;
+ struct work_struct work;
+ enum led_brightness brightnesses[3];
+ enum gt683r_led_mode mode;
+};
+
+static const struct hid_device_id gt683r_led_id[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
+ { }
+};
+
+static void gt683r_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ int i;
+ struct device *dev = led_cdev->dev->parent;
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct gt683r_led *led = hid_get_drvdata(hdev);
+
+ for (i = 0; i < 3; i++) {
+ if (led->led_devs[i] != led_cdev)
+ continue;
+
+ led->brightnesses[i] = brightness;
+ schedule_work(&led->work);
+ break;
+ }
+}
+
+static ssize_t gt683r_show_mode(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct hid_device *hdev =
+ container_of(dev, struct hid_device, dev);
+ struct gt683r_led *led = hid_get_drvdata(hdev);
+
+ if (led->mode == GT683R_LED_NORMAL)
+ return scnprintf(buf, PAGE_SIZE, "0\n");
+ else if (led->mode == GT683R_LED_AUDIO)
+ return scnprintf(buf, PAGE_SIZE, "1\n");
+ else
+ return scnprintf(buf, PAGE_SIZE, "2\n");
+
+}
+
+static ssize_t gt683r_store_mode(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct hid_device *hdev =
+ container_of(dev, struct hid_device, dev);
+ struct gt683r_led *led = hid_get_drvdata(hdev);
+
+ mutex_lock(&led->lock);
+
+ if (!strncmp("0", buf, strlen("0"))) {
+ led->mode = GT683R_LED_NORMAL;
+ } else if (!strncmp("1", buf, strlen("1"))) {
+ led->mode = GT683R_LED_AUDIO;
+ } else if (!strncmp("2", buf, strlen("2"))) {
+ led->mode = GT683R_LED_BREATHING;
+ } else {
+ count = -EINVAL;
+ goto fail;
+ }
+
+ schedule_work(&led->work);
+fail:
+ mutex_unlock(&led->lock);
+
+ return count;
+}
+
+static int gt683r_led_snd_msg(struct gt683r_led *led, u8 *msg)
+{
+ int ret;
+
+ ret = hid_hw_raw_request(led->hdev, msg[0], msg, GT683R_BUFFER_SIZE,
+ HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
+ if (ret != GT683R_BUFFER_SIZE) {
+ hid_err(led->hdev,
+ "failed to send set report request: %i\n", ret);
+ if (ret < 0)
+ return ret;
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void gt683r_led_set(struct gt683r_led *led, u8 leds, u8 mode)
+{
+ u8 *buffer;
+
+ buffer = kzalloc(GT683R_BUFFER_SIZE, GFP_KERNEL);
+ if (!buffer)
+ return;
+
+ buffer[0] = 0x01;
+ buffer[1] = 0x02;
+ buffer[2] = 0x30;
+ buffer[3] = leds;
+ if (gt683r_led_snd_msg(led, buffer))
+ goto fail;
+
+ buffer[2] = 0x20;
+ buffer[3] = mode;
+ buffer[4] = 0x01;
+ gt683r_led_snd_msg(led, buffer);
+
+fail:
+ kfree(buffer);
+}
+
+static void gt683r_led_work(struct work_struct *work)
+{
+ struct gt683r_led *led =
+ container_of(work, struct gt683r_led, work);
+ u8 leds = 0;
+
+ mutex_lock(&led->lock);
+
+ if (led->brightnesses[back])
+ leds |= GT683R_LED_BACK;
+
+ if (led->brightnesses[side])
+ leds |= GT683R_LED_SIDE;
+
+ if (led->brightnesses[front])
+ leds |= GT683R_LED_FRONT;
+
+ if (leds)
+ gt683r_led_set(led, leds, led->mode);
+ else
+ gt683r_led_set(led, leds, GT683R_LED_OFF);
+
+ mutex_unlock(&led->lock);
+}
+
+static DEVICE_ATTR(mode, 0644, gt683r_show_mode, gt683r_store_mode);
+
+static int gt683r_led_probe(struct hid_device *hdev,
+ const struct hid_device_id *id)
+{
+ int ret, i;
+ struct gt683r_led *led;
+
+ led = devm_kzalloc(&hdev->dev, sizeof(struct gt683r_led), GFP_KERNEL);
+ if (!led)
+ return -ENOMEM;
+
+ led->mode = GT683R_LED_NORMAL;
+ led->hdev = hdev;
+ hid_set_drvdata(hdev, led);
+
+ ret = hid_parse(hdev);
+ if (ret) {
+ hid_err(hdev, "hid parsing failed\n");
+ goto fail;
+ }
+
+ ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
+ if (ret) {
+ hid_err(hdev, "hw start failed\n");
+ goto fail;
+ }
+
+ for (i = 0; i < 3; i++) {
+ led->led_devs[i] = kzalloc(sizeof(struct led_classdev) +
+ strlen(gt683r_panel_names[i]) + 1,
+ GFP_KERNEL);
+ if (!led->led_devs[i])
+ goto fail2;
+ led->led_devs[i]->name = gt683r_panel_names[i];
+ led->led_devs[i]->max_brightness = 1;
+ led->led_devs[i]->brightness_set = gt683r_brightness_set;
+ ret = led_classdev_register(&hdev->dev, led->led_devs[i]);
+ if (ret) {
+ hid_err(hdev, "could not register led device\n");
+ goto fail2;
+ }
+ }
+
+ ret = device_create_file(&led->hdev->dev,
+ &dev_attr_mode);
+ if (ret) {
+ hid_err(hdev, "could not make mode attribute file\n");
+ goto fail2;
+ }
+
+ mutex_init(&led->lock);
+ INIT_WORK(&led->work, gt683r_led_work);
+
+ return 0;
+
+fail2:
+ while (i-- > 0) {
+ led_classdev_unregister(led->led_devs[i]);
+ kfree(led->led_devs[i]);
+ }
+ hid_hw_stop(hdev);
+fail:
+ return ret;
+}
+
+static void gt683r_led_remove(struct hid_device *hdev)
+{
+ int i;
+ struct gt683r_led *led = hid_get_drvdata(hdev);
+
+ for (i = 0; i < 3; i++) {
+ led_classdev_unregister(led->led_devs[i]);
+ kfree(led->led_devs[i]);
+ }
+ cancel_work_sync(&led->work);
+ device_remove_file(&hdev->dev,
+ &dev_attr_mode);
+ hid_hw_stop(hdev);
+}
+
+static struct hid_driver gt683r_led_driver = {
+ .probe = gt683r_led_probe,
+ .remove = gt683r_led_remove,
+ .name = "gt683r_led",
+ .id_table = gt683r_led_id,
+};
+
+module_hid_driver(gt683r_led_driver);
+
+MODULE_AUTHOR("Janne Kanniainen");
+MODULE_DESCRIPTION("MSI GT683R led driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 34bb220..3692d37 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -641,7 +641,7 @@
#define USB_DEVICE_ID_GENIUS_KB29E 0x3004

#define USB_VENDOR_ID_MSI 0x1770
-#define USB_DEVICE_ID_MSI_GX680R_LED_PANEL 0xff00
+#define USB_DEVICE_ID_MSI_GT683R_LED_PANEL 0xff00

#define USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR 0x0400
#define USB_DEVICE_ID_N_S_HARMONY 0xc359
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
index 8e4ddb3..c640e1d 100644
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -73,7 +73,7 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET },
- { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GX680R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
+ { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_NEXIO, USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_MOUSE, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NO_INIT_REPORTS },
--
1.9.2

2014-06-13 07:54:52

by Johan Hovold

[permalink] [raw]
Subject: Re: [PATCH v5] leds: USB: HID: Add support for MSI GT683R led panels

On Thu, Jun 12, 2014 at 11:34:12PM +0300, Janne Kanniainen wrote:
> This driver adds support for USB controlled led panels that exists in
> MSI GT683R laptop
>
> Signed-off-by: Janne Kanniainen <[email protected]>
> ---
> Changes in v2:
> - sorted headers to alphabetic order
> - using devm_kzalloc
> - using BIT(n)
> - using usb_control_msg instead of usb_submit_urb
> - removing unneeded code
>
> Changes in v3:
> - implemented as HID device
> - some cleanups and bug fixes
>
> Changes in v4:
> - more cleanups
> - support for selecting leds
> - suppport for selecting status
>
> Changes in v5:
> - mode attribute documented under Documentation/ABI
> - made array for led_classdev
> - led devices uses now recommended naming scheme
>
> .../ABI/testing/sysfs-class-hid-driver-gt683r | 10 +
> drivers/hid/Kconfig | 11 +
> drivers/hid/Makefile | 1 +
> drivers/hid/hid-core.c | 1 +
> drivers/hid/hid-gt683r.c | 294 +++++++++++++++++++++
> drivers/hid/hid-ids.h | 2 +-
> drivers/hid/usbhid/hid-quirks.c | 2 +-
> 7 files changed, 319 insertions(+), 2 deletions(-)
> create mode 100644 Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
> create mode 100644 drivers/hid/hid-gt683r.c
>
> diff --git a/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
> new file mode 100644
> index 0000000..c4d604e
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
> @@ -0,0 +1,10 @@
> +What: /sys/class/hidraw/<hidraw>/device/state

You should probably stick to "mode" (rather than "state") throughout (it
seems you just forgot to update a few uses).

> +Date: Jun 2014
> +KernelVersion: 3.15

This should be 3.17.

> +Contact: Janne Kanniainen <[email protected]>
> +Description:
> + Set the mode of LEDs
> +
> + 0 - normal
> + 1 - audio
> + 2 - breathing

Perhaps expand this with a short paragraph describing the different
modes.

> diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
> index 7af9d0b..d93e0ae 100644
> --- a/drivers/hid/Kconfig
> +++ b/drivers/hid/Kconfig
> @@ -261,6 +261,17 @@ config HOLTEK_FF
> Say Y here if you have a Holtek On Line Grip based game controller
> and want to have force feedback support for it.
>
> +config HID_GT683R
> + tristate "MSI GT68xR LED support"
> + depends on LEDS_CLASS && USB_HID
> + ---help---
> + Say Y here if you want to enable support for the MSI GT68xR LEDS
> +
> + This driver support following states normal, breathing and audio.

You could also use "modes" and expand this with the same description.

> + You can also select which leds you want to enable.
> + Currently the following devices are know to be supported:
> + - MSI GT683R
> +
> config HID_HUION
> tristate "Huion tablets"
> depends on USB_HID
> diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
> index fc712dd..7129311 100644
> --- a/drivers/hid/Makefile
> +++ b/drivers/hid/Makefile
> @@ -48,6 +48,7 @@ obj-$(CONFIG_HID_EMS_FF) += hid-emsff.o
> obj-$(CONFIG_HID_ELECOM) += hid-elecom.o
> obj-$(CONFIG_HID_ELO) += hid-elo.o
> obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o
> +obj-$(CONFIG_HID_GT683R) += hid-gt683r.o
> obj-$(CONFIG_HID_GYRATION) += hid-gyration.o
> obj-$(CONFIG_HID_HOLTEK) += hid-holtek-kbd.o
> obj-$(CONFIG_HID_HOLTEK) += hid-holtek-mouse.o
> diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
> index da52279..ec88fdb 100644
> --- a/drivers/hid/hid-core.c
> +++ b/drivers/hid/hid-core.c
> @@ -1827,6 +1827,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
> { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) },
> { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_OFFICE_KB) },
> { HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) },
> + { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
> { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) },
> { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_1) },
> { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_2) },
> diff --git a/drivers/hid/hid-gt683r.c b/drivers/hid/hid-gt683r.c
> new file mode 100644
> index 0000000..6dffb76
> --- /dev/null
> +++ b/drivers/hid/hid-gt683r.c
> @@ -0,0 +1,294 @@
> +/*
> + * MSI GT683R led driver
> + *
> + * Copyright (c) 2014 Janne Kanniainen <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/device.h>
> +#include <linux/hid.h>
> +#include <linux/kernel.h>
> +#include <linux/leds.h>
> +#include <linux/module.h>
> +
> +#include "hid-ids.h"
> +
> +#define GT683R_LED_BACK BIT(0)
> +#define GT683R_LED_SIDE BIT(1)
> +#define GT683R_LED_FRONT BIT(2)
> +
> +#define GT683R_BUFFER_SIZE 8
> +
> +/*
> + * GT683R_LED_OFF: all LEDs are off
> + * GT683R_LED_AUDIO: the status of LEDs depends
> + * on sound level
> + * GT683R_LED_BREATHING: LEDs brightness varies
> + * at human breathing rate
> + * GT683R_LED_NORMAL: LEDs are on
> + */
> +enum gt683r_led_mode {
> + GT683R_LED_OFF = 0,
> + GT683R_LED_AUDIO = 2,
> + GT683R_LED_BREATHING = 3,
> + GT683R_LED_NORMAL = 5
> +};
> +
> +enum gt683r_panels {
> + back,
> + side,
> + front,
> +};

How about calling these GT683R_LED_BACK, etc, and simply do
BIT(GT683R_LED_BACK) in gt683r_led_setset_led below (and drop the
bitmask defines above).

You should also add a count enum/define rather than hard-code 3
throughout the driver, for example:

enum gt683r_panels {
GT683R_LED_BACK = 0,
GT683R_LED_SIDE = 1,
GT683R_LED_FRONT = 2,
GT683R_LED_COUNT
};

> +
> +const char *gt683r_panel_names[] = {
> + "gt683r::back",
> + "gt683r::side",
> + "gt683r::front",
> +};

Your forgot to declare this static.

I don't think you should hard-code the device-part (i.e. "gt683r") but
rather derive it from the hid device (as in the driver I referred to as
an example). Just store the description here (e.g. "back"). More details
below.

> +
> +struct gt683r_led {
> + struct hid_device *hdev;
> + struct led_classdev *led_devs[3];

You could keep these as embedded structs (an array of structs) rather
than do individual allocations in probe.

> + struct mutex lock;
> + struct work_struct work;
> + enum led_brightness brightnesses[3];
> + enum gt683r_led_mode mode;
> +};
> +
> +static const struct hid_device_id gt683r_led_id[] = {
> + { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
> + { }
> +};
> +
> +static void gt683r_brightness_set(struct led_classdev *led_cdev,
> + enum led_brightness brightness)
> +{
> + int i;
> + struct device *dev = led_cdev->dev->parent;
> + struct hid_device *hdev = container_of(dev, struct hid_device, dev);
> + struct gt683r_led *led = hid_get_drvdata(hdev);
> +
> + for (i = 0; i < 3; i++) {
> + if (led->led_devs[i] != led_cdev)
> + continue;
> +
> + led->brightnesses[i] = brightness;
> + schedule_work(&led->work);
> + break;

I'd move the assignment and work scheduling out of the loop (and add an
explicit check for LED-not-found, if at all needed).

> + }
> +}
> +
> +static ssize_t gt683r_show_mode(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> +{
> + struct hid_device *hdev =
> + container_of(dev, struct hid_device, dev);
> + struct gt683r_led *led = hid_get_drvdata(hdev);
> +
> + if (led->mode == GT683R_LED_NORMAL)
> + return scnprintf(buf, PAGE_SIZE, "0\n");
> + else if (led->mode == GT683R_LED_AUDIO)
> + return scnprintf(buf, PAGE_SIZE, "1\n");
> + else
> + return scnprintf(buf, PAGE_SIZE, "2\n");

How about mapping led->mode to sysfs mode in a temporary variable and
the just do one:

return scnprintf(buf, PAGE_SIZE, "%u\n", sysfs_mode);

at the end?

> +
> +}
> +
> +static ssize_t gt683r_store_mode(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + struct hid_device *hdev =
> + container_of(dev, struct hid_device, dev);
> + struct gt683r_led *led = hid_get_drvdata(hdev);
> +
> + mutex_lock(&led->lock);
> +
> + if (!strncmp("0", buf, strlen("0"))) {
> + led->mode = GT683R_LED_NORMAL;
> + } else if (!strncmp("1", buf, strlen("1"))) {
> + led->mode = GT683R_LED_AUDIO;
> + } else if (!strncmp("2", buf, strlen("2"))) {
> + led->mode = GT683R_LED_BREATHING;
> + } else {
> + count = -EINVAL;
> + goto fail;
> + }

Here you should use snprintf to parse buf as I already mentioned. With
the current implementation "22" would be accepted as a valid mode.

> +
> + schedule_work(&led->work);
> +fail:
> + mutex_unlock(&led->lock);

You should unlock before scheduling.

> +
> + return count;
> +}
> +
> +static int gt683r_led_snd_msg(struct gt683r_led *led, u8 *msg)
> +{
> + int ret;
> +
> + ret = hid_hw_raw_request(led->hdev, msg[0], msg, GT683R_BUFFER_SIZE,
> + HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
> + if (ret != GT683R_BUFFER_SIZE) {
> + hid_err(led->hdev,
> + "failed to send set report request: %i\n", ret);
> + if (ret < 0)
> + return ret;
> + return -EIO;
> + }
> +
> + return 0;
> +}
> +
> +static void gt683r_led_set(struct gt683r_led *led, u8 leds, u8 mode)
> +{
> + u8 *buffer;
> +
> + buffer = kzalloc(GT683R_BUFFER_SIZE, GFP_KERNEL);
> + if (!buffer)
> + return;
> +
> + buffer[0] = 0x01;
> + buffer[1] = 0x02;
> + buffer[2] = 0x30;
> + buffer[3] = leds;
> + if (gt683r_led_snd_msg(led, buffer))
> + goto fail;
> +
> + buffer[2] = 0x20;
> + buffer[3] = mode;
> + buffer[4] = 0x01;
> + gt683r_led_snd_msg(led, buffer);

Ok, so you decided to continue setting mode on every LED brightness
update. That should be fine, but you never answered my question about
whether it is necessary?

> +
> +fail:
> + kfree(buffer);
> +}
> +
> +static void gt683r_led_work(struct work_struct *work)
> +{
> + struct gt683r_led *led =
> + container_of(work, struct gt683r_led, work);
> + u8 leds = 0;
> +
> + mutex_lock(&led->lock);
> +
> + if (led->brightnesses[back])
> + leds |= GT683R_LED_BACK;
> +
> + if (led->brightnesses[side])
> + leds |= GT683R_LED_SIDE;
> +
> + if (led->brightnesses[front])
> + leds |= GT683R_LED_FRONT;
> +
> + if (leds)
> + gt683r_led_set(led, leds, led->mode);
> + else
> + gt683r_led_set(led, leds, GT683R_LED_OFF);
> +
> + mutex_unlock(&led->lock);
> +}
> +
> +static DEVICE_ATTR(mode, 0644, gt683r_show_mode, gt683r_store_mode);

It is even recommended to use the new DEVICE_ATTR_RW macro here rather
(forgot about that).

> +
> +static int gt683r_led_probe(struct hid_device *hdev,
> + const struct hid_device_id *id)
> +{
> + int ret, i;
> + struct gt683r_led *led;
> +
> + led = devm_kzalloc(&hdev->dev, sizeof(struct gt683r_led), GFP_KERNEL);
> + if (!led)
> + return -ENOMEM;
> +
> + led->mode = GT683R_LED_NORMAL;
> + led->hdev = hdev;
> + hid_set_drvdata(hdev, led);
> +
> + ret = hid_parse(hdev);
> + if (ret) {
> + hid_err(hdev, "hid parsing failed\n");
> + goto fail;
> + }
> +
> + ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
> + if (ret) {
> + hid_err(hdev, "hw start failed\n");
> + goto fail;
> + }
> +
> + for (i = 0; i < 3; i++) {
> + led->led_devs[i] = kzalloc(sizeof(struct led_classdev) +
> + strlen(gt683r_panel_names[i]) + 1,

Why are you allocating space for the panel names here? Oh, now I see,
that's how it's done in the example I referred to. :)

Just embed the struct led_classdevs directly in struct gt683r_led, and
use devm_kzalloc(&hdev->dev, ...) to allocate the name buffers.

> + GFP_KERNEL);
> + if (!led->led_devs[i])
> + goto fail2;
> + led->led_devs[i]->name = gt683r_panel_names[i];

The other hid leds use dev_name(&hdev->dev) for the device name part of
the name. So determine the buffer length:

strlen(dev_name(&hdev->dev)) + strlen(gt683r_panel_names[i]) + 3

and use snprintf with the following format "%s::%s".

> + led->led_devs[i]->max_brightness = 1;
> + led->led_devs[i]->brightness_set = gt683r_brightness_set;
> + ret = led_classdev_register(&hdev->dev, led->led_devs[i]);
> + if (ret) {
> + hid_err(hdev, "could not register led device\n");
> + goto fail2;
> + }
> + }
> +
> + ret = device_create_file(&led->hdev->dev,
> + &dev_attr_mode);
> + if (ret) {
> + hid_err(hdev, "could not make mode attribute file\n");
> + goto fail2;
> + }
> +
> + mutex_init(&led->lock);
> + INIT_WORK(&led->work, gt683r_led_work);
> +
> + return 0;
> +
> +fail2:
> + while (i-- > 0) {

Please use a for-loop instead (for readability).

> + led_classdev_unregister(led->led_devs[i]);
> + kfree(led->led_devs[i]);

This would currently leak memory if led_classdev_register fails, but
will work if you use devm_kzalloc as suggested above.

> + }
> + hid_hw_stop(hdev);
> +fail:
> + return ret;
> +}
> +
> +static void gt683r_led_remove(struct hid_device *hdev)
> +{
> + int i;
> + struct gt683r_led *led = hid_get_drvdata(hdev);
> +
> + for (i = 0; i < 3; i++) {
> + led_classdev_unregister(led->led_devs[i]);
> + kfree(led->led_devs[i]);
> + }
> + cancel_work_sync(&led->work);
> + device_remove_file(&hdev->dev,
> + &dev_attr_mode);
> + hid_hw_stop(hdev);
> +}
> +
> +static struct hid_driver gt683r_led_driver = {
> + .probe = gt683r_led_probe,
> + .remove = gt683r_led_remove,
> + .name = "gt683r_led",
> + .id_table = gt683r_led_id,
> +};
> +
> +module_hid_driver(gt683r_led_driver);
> +
> +MODULE_AUTHOR("Janne Kanniainen");
> +MODULE_DESCRIPTION("MSI GT683R led driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
> index 34bb220..3692d37 100644
> --- a/drivers/hid/hid-ids.h
> +++ b/drivers/hid/hid-ids.h
> @@ -641,7 +641,7 @@
> #define USB_DEVICE_ID_GENIUS_KB29E 0x3004
>
> #define USB_VENDOR_ID_MSI 0x1770
> -#define USB_DEVICE_ID_MSI_GX680R_LED_PANEL 0xff00
> +#define USB_DEVICE_ID_MSI_GT683R_LED_PANEL 0xff00
>
> #define USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR 0x0400
> #define USB_DEVICE_ID_N_S_HARMONY 0xc359
> diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
> index 8e4ddb3..c640e1d 100644
> --- a/drivers/hid/usbhid/hid-quirks.c
> +++ b/drivers/hid/usbhid/hid-quirks.c
> @@ -73,7 +73,7 @@ static const struct hid_blacklist {
> { USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS },
> { USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET },
> { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET },
> - { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GX680R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
> + { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
> { USB_VENDOR_ID_NEXIO, USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750, HID_QUIRK_NO_INIT_REPORTS },
> { USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_MOUSE, HID_QUIRK_NO_INIT_REPORTS },
> { USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NO_INIT_REPORTS },

You're almost done. One last update? :)

Thanks,
Johan

2014-06-13 17:19:30

by Janne Kanniainen

[permalink] [raw]
Subject: Re: [PATCH v5] leds: USB: HID: Add support for MSI GT683R led panels

> Ok, so you decided to continue setting mode on every LED brightness
> update. That should be fine, but you never answered my question about
> whether it is necessary?

I decided to do it that way because official driver did it as well. I
can check if it is necessary.

> You're almost done. One last update? :)

Yeah, I hope so :)

2014-06-14 22:42:47

by Pavel Machek

[permalink] [raw]
Subject: Re: [PATCH v5] leds: USB: HID: Add support for MSI GT683R led panels

On Thu 2014-06-12 23:34:12, Janne Kanniainen wrote:
> This driver adds support for USB controlled led panels that exists in
> MSI GT683R laptop


Hi!

> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
> @@ -0,0 +1,10 @@
> +What: /sys/class/hidraw/<hidraw>/device/state
> +Date: Jun 2014
> +KernelVersion: 3.15
> +Contact: Janne Kanniainen <[email protected]>
> +Description:
> + Set the mode of LEDs
> +
> + 0 - normal
> + 1 - audio
> + 2 - breathing

THat's some strange interface. Don't we normally use led triggers for this?

And the mode of the LED should really be in /sys/class/leds, not in hidraw somewhere...

Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

2014-06-14 23:23:28

by Janne Kanniainen

[permalink] [raw]
Subject: Re: [PATCH v5] leds: USB: HID: Add support for MSI GT683R led panels

> Hi!

Hi.

>> --- /dev/null
>> +++ b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
>> @@ -0,0 +1,10 @@
>> +What: /sys/class/hidraw/<hidraw>/device/state
>> +Date: Jun 2014
>> +KernelVersion: 3.15
>> +Contact: Janne Kanniainen <[email protected]>
>> +Description:
>> + Set the mode of LEDs
>> +
>> + 0 - normal
>> + 1 - audio
>> + 2 - breathing
>
> THat's some strange interface. Don't we normally use led triggers for this?

I can implement it that way, if you all think that it is correct way.
What do Jiri and Johan thinks of it?

> And the mode of the LED should really be in /sys/class/leds, not in hidraw somewhere...

The problem is that all panels can only be in one mode at the time.
For example front panel can't be in breathing mode while side panel is
in normal mode.


Janne

2014-06-15 14:59:43

by Janne Kanniainen

[permalink] [raw]
Subject: Re: [PATCH v5] leds: USB: HID: Add support for MSI GT683R led panels

>> Ok, so you decided to continue setting mode on every LED brightness
>> update. That should be fine, but you never answered my question about
>> whether it is necessary?
>
> I decided to do it that way because official driver did it as well. I
> can check if it is necessary.

Ok, I checked this one and every time you update LEDs brightness you
will need to update mode also.

2014-06-16 07:40:40

by Johan Hovold

[permalink] [raw]
Subject: Re: [PATCH v5] leds: USB: HID: Add support for MSI GT683R led panels

On Sun, Jun 15, 2014 at 05:59:40PM +0300, Janne Kanniainen wrote:
> >> Ok, so you decided to continue setting mode on every LED brightness
> >> update. That should be fine, but you never answered my question about
> >> whether it is necessary?
> >
> > I decided to do it that way because official driver did it as well. I
> > can check if it is necessary.
>
> Ok, I checked this one and every time you update LEDs brightness you
> will need to update mode also.

Ok, great. Then we know.

Johan

2014-06-16 07:45:46

by Johan Hovold

[permalink] [raw]
Subject: Re: [PATCH v5] leds: USB: HID: Add support for MSI GT683R led panels

On Sun, Jun 15, 2014 at 02:23:25AM +0300, Janne Kanniainen wrote:
> > Hi!
>
> Hi.
>
> >> --- /dev/null
> >> +++ b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
> >> @@ -0,0 +1,10 @@
> >> +What: /sys/class/hidraw/<hidraw>/device/state
> >> +Date: Jun 2014
> >> +KernelVersion: 3.15
> >> +Contact: Janne Kanniainen <[email protected]>
> >> +Description:
> >> + Set the mode of LEDs
> >> +
> >> + 0 - normal
> >> + 1 - audio
> >> + 2 - breathing
> >
> > THat's some strange interface. Don't we normally use led triggers
> > for this?
>
> I can implement it that way, if you all think that it is correct way.
> What do Jiri and Johan thinks of it?
>
> > And the mode of the LED should really be in /sys/class/leds, not in
> > hidraw somewhere...
>
> The problem is that all panels can only be in one mode at the time.
> For example front panel can't be in breathing mode while side panel is
> in normal mode.

As Janne explained above, it's really an attribute of the parent device
and not the individual LEDs. The latter could still use the software
timer trigger to blink independently.

Johan

2014-06-16 17:24:13

by Janne Kanniainen

[permalink] [raw]
Subject: [PATCH v6] leds: USB: HID: Add support for MSI GT683R led panels

This driver adds support for USB controlled led panels that exists in
MSI GT683R laptop

Signed-off-by: Janne Kanniainen <[email protected]>
---
Changes in v2:
- sorted headers to alphabetic order
- using devm_kzalloc
- using BIT(n)
- using usb_control_msg instead of usb_submit_urb
- removing unneeded code

Changes in v3:
- implemented as HID device
- some cleanups and bug fixes

Changes in v4:
- more cleanups
- support for selecting leds
- suppport for selecting status

Changes in v5:
- mode attribute documented under Documentation/ABI
- made array for led_classdev
- led devices uses now recommended naming scheme

Changes in v6:
- flush_work added
- using hid device name instead of hard coded gt683r
- allocating name buffers with devm_kzalloc

.../ABI/testing/sysfs-class-hid-driver-gt683r | 14 +
drivers/hid/Kconfig | 16 ++
drivers/hid/Makefile | 1 +
drivers/hid/hid-core.c | 1 +
drivers/hid/hid-gt683r.c | 318 +++++++++++++++++++++
drivers/hid/hid-ids.h | 2 +-
drivers/hid/usbhid/hid-quirks.c | 2 +-
7 files changed, 352 insertions(+), 2 deletions(-)
create mode 100644 Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
create mode 100644 drivers/hid/hid-gt683r.c

diff --git a/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
new file mode 100644
index 0000000..6d0bd80
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
@@ -0,0 +1,14 @@
+What: /sys/class/hidraw/<hidraw>/device/mode
+Date: Jun 2014
+KernelVersion: 3.17
+Contact: Janne Kanniainen <[email protected]>
+Description:
+ Set the mode of LEDs
+
+ 0 - normal
+ 1 - audio
+ 2 - breathing
+
+ Normal: LEDs are on
+ Audio: LEDs brightness depends on sound level
+ Breathing: LEDs brightness varies at human breathing rate
\ No newline at end of file
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 7af9d0b..b88cabd 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -261,6 +261,22 @@ config HOLTEK_FF
Say Y here if you have a Holtek On Line Grip based game controller
and want to have force feedback support for it.

+config HID_GT683R
+ tristate "MSI GT68xR LED support"
+ depends on LEDS_CLASS && USB_HID
+ ---help---
+ Say Y here if you want to enable support for the MSI GT68xR LEDS
+
+ This driver support following modes:
+ - Normal: LEDs are on
+ - Audio: LEDs brightness depends on sound level
+ - Breathing: LEDs brightness varies at human breathing rate
+
+ You can also select which leds you want to enable.
+
+ Currently the following devices are know to be supported:
+ - MSI GT683R
+
config HID_HUION
tristate "Huion tablets"
depends on USB_HID
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index fc712dd..7129311 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -48,6 +48,7 @@ obj-$(CONFIG_HID_EMS_FF) += hid-emsff.o
obj-$(CONFIG_HID_ELECOM) += hid-elecom.o
obj-$(CONFIG_HID_ELO) += hid-elo.o
obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o
+obj-$(CONFIG_HID_GT683R) += hid-gt683r.o
obj-$(CONFIG_HID_GYRATION) += hid-gyration.o
obj-$(CONFIG_HID_HOLTEK) += hid-holtek-kbd.o
obj-$(CONFIG_HID_HOLTEK) += hid-holtek-mouse.o
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index da52279..ec88fdb 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1827,6 +1827,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_OFFICE_KB) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) },
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_1) },
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_2) },
diff --git a/drivers/hid/hid-gt683r.c b/drivers/hid/hid-gt683r.c
new file mode 100644
index 0000000..132cc54
--- /dev/null
+++ b/drivers/hid/hid-gt683r.c
@@ -0,0 +1,318 @@
+/*
+ * MSI GT683R led driver
+ *
+ * Copyright (c) 2014 Janne Kanniainen <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+#define GT683R_BUFFER_SIZE 8
+
+/*
+ * GT683R_LED_OFF: all LEDs are off
+ * GT683R_LED_AUDIO: LEDs brightness depends
+ * on sound level
+ * GT683R_LED_BREATHING: LEDs brightness varies
+ * at human breathing rate
+ * GT683R_LED_NORMAL: LEDs are on
+ */
+enum gt683r_led_mode {
+ GT683R_LED_OFF = 0,
+ GT683R_LED_AUDIO = 2,
+ GT683R_LED_BREATHING = 3,
+ GT683R_LED_NORMAL = 5
+};
+
+enum gt683r_panels {
+ GT683R_LED_BACK,
+ GT683R_LED_SIDE,
+ GT683R_LED_FRONT,
+ GT683R_LED_COUNT,
+};
+
+static const char * const gt683r_panel_names[] = {
+ "back",
+ "side",
+ "front",
+};
+
+struct gt683r_led {
+ struct hid_device *hdev;
+ struct led_classdev led_devs[GT683R_LED_COUNT];
+ struct mutex lock;
+ struct work_struct work;
+ enum led_brightness brightnesses[GT683R_LED_COUNT];
+ enum gt683r_led_mode mode;
+};
+
+static const struct hid_device_id gt683r_led_id[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
+ { }
+};
+
+static void gt683r_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ int i;
+ struct device *dev = led_cdev->dev->parent;
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct gt683r_led *led = hid_get_drvdata(hdev);
+
+ for (i = 0; i < GT683R_LED_COUNT; i++) {
+ if (&led->led_devs[i] == led_cdev)
+ break;
+ }
+
+ if (i < GT683R_LED_COUNT) {
+ led->brightnesses[i] = brightness;
+ schedule_work(&led->work);
+ }
+}
+
+static ssize_t mode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ u8 sysfs_mode;
+ struct hid_device *hdev =
+ container_of(dev, struct hid_device, dev);
+ struct gt683r_led *led = hid_get_drvdata(hdev);
+
+ if (led->mode == GT683R_LED_NORMAL)
+ sysfs_mode = 0;
+ else if (led->mode == GT683R_LED_AUDIO)
+ sysfs_mode = 1;
+ else
+ sysfs_mode = 2;
+
+ return scnprintf(buf, PAGE_SIZE, "%u\n", sysfs_mode);
+}
+
+static ssize_t mode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ u8 sysfs_mode;
+ struct hid_device *hdev =
+ container_of(dev, struct hid_device, dev);
+ struct gt683r_led *led = hid_get_drvdata(hdev);
+
+ mutex_lock(&led->lock);
+
+ if (kstrtou8(buf, 10, &sysfs_mode) || sysfs_mode > 2) {
+ count = -EINVAL;
+ goto fail;
+ }
+
+ if (sysfs_mode == 0)
+ led->mode = GT683R_LED_NORMAL;
+ else if (sysfs_mode == 1)
+ led->mode = GT683R_LED_AUDIO;
+ else
+ led->mode = GT683R_LED_BREATHING;
+
+fail:
+ mutex_unlock(&led->lock);
+
+ if (count != -EINVAL)
+ schedule_work(&led->work);
+
+ return count;
+}
+
+static int gt683r_led_snd_msg(struct gt683r_led *led, u8 *msg)
+{
+ int ret;
+
+ ret = hid_hw_raw_request(led->hdev, msg[0], msg, GT683R_BUFFER_SIZE,
+ HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
+ if (ret != GT683R_BUFFER_SIZE) {
+ hid_err(led->hdev,
+ "failed to send set report request: %i\n", ret);
+ if (ret < 0)
+ return ret;
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int gt683r_leds_set(struct gt683r_led *led, u8 leds)
+{
+ int ret;
+ u8 *buffer;
+
+ buffer = kzalloc(GT683R_BUFFER_SIZE, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
+ buffer[0] = 0x01;
+ buffer[1] = 0x02;
+ buffer[2] = 0x30;
+ buffer[3] = leds;
+ ret = gt683r_led_snd_msg(led, buffer);
+
+ kfree(buffer);
+ return ret;
+}
+
+static int gt683r_mode_set(struct gt683r_led *led, u8 mode)
+{
+ int ret;
+ u8 *buffer;
+
+ buffer = kzalloc(GT683R_BUFFER_SIZE, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
+ buffer[0] = 0x01;
+ buffer[1] = 0x02;
+ buffer[2] = 0x20;
+ buffer[3] = mode;
+ buffer[4] = 0x01;
+ ret = gt683r_led_snd_msg(led, buffer);
+
+ kfree(buffer);
+ return ret;
+}
+
+static void gt683r_led_work(struct work_struct *work)
+{
+ u8 leds = 0, mode;
+ struct gt683r_led *led =
+ container_of(work, struct gt683r_led, work);
+
+ mutex_lock(&led->lock);
+
+ if (led->brightnesses[GT683R_LED_BACK])
+ leds |= BIT(GT683R_LED_BACK);
+
+ if (led->brightnesses[GT683R_LED_SIDE])
+ leds |= BIT(GT683R_LED_SIDE);
+
+ if (led->brightnesses[GT683R_LED_FRONT])
+ leds |= BIT(GT683R_LED_FRONT);
+
+ if (gt683r_leds_set(led, leds))
+ goto fail;
+
+ if (leds)
+ mode = led->mode;
+ else
+ mode = GT683R_LED_OFF;
+
+ gt683r_mode_set(led, mode);
+fail:
+ mutex_unlock(&led->lock);
+}
+
+static DEVICE_ATTR_RW(mode);
+
+static int gt683r_led_probe(struct hid_device *hdev,
+ const struct hid_device_id *id)
+{
+ int ret, i, name_sz;
+ char *name;
+ struct gt683r_led *led;
+
+ led = devm_kzalloc(&hdev->dev, sizeof(struct gt683r_led), GFP_KERNEL);
+ if (!led)
+ return -ENOMEM;
+
+ led->mode = GT683R_LED_NORMAL;
+ led->hdev = hdev;
+ hid_set_drvdata(hdev, led);
+
+ ret = hid_parse(hdev);
+ if (ret) {
+ hid_err(hdev, "hid parsing failed\n");
+ goto fail;
+ }
+
+ ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
+ if (ret) {
+ hid_err(hdev, "hw start failed\n");
+ goto fail;
+ }
+
+ for (i = 0; i < GT683R_LED_COUNT; i++) {
+ name_sz = strlen(dev_name(&hdev->dev)) +
+ strlen(gt683r_panel_names[i]) + 3;
+
+ name = devm_kzalloc(&hdev->dev, sizeof(char) * name_sz,
+ GFP_KERNEL);
+
+ snprintf(name, name_sz, "%s::%s",
+ dev_name(&hdev->dev), gt683r_panel_names[i]);
+ led->led_devs[i].name = name;
+ led->led_devs[i].max_brightness = 1;
+ led->led_devs[i].brightness_set = gt683r_brightness_set;
+ ret = led_classdev_register(&hdev->dev, &led->led_devs[i]);
+ if (ret) {
+ hid_err(hdev, "could not register led device\n");
+ goto fail2;
+ }
+ }
+
+ ret = device_create_file(&led->hdev->dev,
+ &dev_attr_mode);
+ if (ret) {
+ hid_err(hdev, "could not make mode attribute file\n");
+ goto fail2;
+ }
+
+ mutex_init(&led->lock);
+ INIT_WORK(&led->work, gt683r_led_work);
+
+ return 0;
+
+fail2:
+ for (; i > 0; i--)
+ led_classdev_unregister(&led->led_devs[i]);
+ hid_hw_stop(hdev);
+fail:
+ return ret;
+}
+
+static void gt683r_led_remove(struct hid_device *hdev)
+{
+ int i;
+ struct gt683r_led *led = hid_get_drvdata(hdev);
+
+ for (i = 0; i < GT683R_LED_COUNT; i++)
+ led_classdev_unregister(&led->led_devs[i]);
+ device_remove_file(&hdev->dev,
+ &dev_attr_mode);
+ flush_work(&led->work);
+ hid_hw_stop(hdev);
+}
+
+static struct hid_driver gt683r_led_driver = {
+ .probe = gt683r_led_probe,
+ .remove = gt683r_led_remove,
+ .name = "gt683r_led",
+ .id_table = gt683r_led_id,
+};
+
+module_hid_driver(gt683r_led_driver);
+
+MODULE_AUTHOR("Janne Kanniainen");
+MODULE_DESCRIPTION("MSI GT683R led driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 34bb220..3692d37 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -641,7 +641,7 @@
#define USB_DEVICE_ID_GENIUS_KB29E 0x3004

#define USB_VENDOR_ID_MSI 0x1770
-#define USB_DEVICE_ID_MSI_GX680R_LED_PANEL 0xff00
+#define USB_DEVICE_ID_MSI_GT683R_LED_PANEL 0xff00

#define USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR 0x0400
#define USB_DEVICE_ID_N_S_HARMONY 0xc359
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
index 8e4ddb3..c640e1d 100644
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -73,7 +73,7 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET },
- { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GX680R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
+ { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_NEXIO, USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_MOUSE, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NO_INIT_REPORTS },
--
1.9.2

2014-06-16 22:02:13

by Janne Kanniainen

[permalink] [raw]
Subject: [PATCH v6] leds: USB: HID: Add support for MSI GT683R led panels

This driver adds support for USB controlled led panels that exists in
MSI GT683R laptop

Signed-off-by: Janne Kanniainen <[email protected]>
---
Changes in v2:
- sorted headers to alphabetic order
- using devm_kzalloc
- using BIT(n)
- using usb_control_msg instead of usb_submit_urb
- removing unneeded code

Changes in v3:
- implemented as HID device
- some cleanups and bug fixes

Changes in v4:
- more cleanups
- support for selecting leds
- suppport for selecting status

Changes in v5:
- mode attribute documented under Documentation/ABI
- made array for led_classdev
- led devices uses now recommended naming scheme

Changes in v6:
- flush_work added
- using hid device name instead of hard coded gt683r
- allocating name buffers with devm_kzalloc

There was a bug with "for", so I fixed it.

.../ABI/testing/sysfs-class-hid-driver-gt683r | 14 +
drivers/hid/Kconfig | 16 ++
drivers/hid/Makefile | 1 +
drivers/hid/hid-core.c | 1 +
drivers/hid/hid-gt683r.c | 318 +++++++++++++++++++++
drivers/hid/hid-ids.h | 2 +-
drivers/hid/usbhid/hid-quirks.c | 2 +-
7 files changed, 352 insertions(+), 2 deletions(-)
create mode 100644 Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
create mode 100644 drivers/hid/hid-gt683r.c

diff --git a/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
new file mode 100644
index 0000000..6d0bd80
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
@@ -0,0 +1,14 @@
+What: /sys/class/hidraw/<hidraw>/device/mode
+Date: Jun 2014
+KernelVersion: 3.17
+Contact: Janne Kanniainen <[email protected]>
+Description:
+ Set the mode of LEDs
+
+ 0 - normal
+ 1 - audio
+ 2 - breathing
+
+ Normal: LEDs are on
+ Audio: LEDs brightness depends on sound level
+ Breathing: LEDs brightness varies at human breathing rate
\ No newline at end of file
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 7af9d0b..b88cabd 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -261,6 +261,22 @@ config HOLTEK_FF
Say Y here if you have a Holtek On Line Grip based game controller
and want to have force feedback support for it.

+config HID_GT683R
+ tristate "MSI GT68xR LED support"
+ depends on LEDS_CLASS && USB_HID
+ ---help---
+ Say Y here if you want to enable support for the MSI GT68xR LEDS
+
+ This driver support following modes:
+ - Normal: LEDs are on
+ - Audio: LEDs brightness depends on sound level
+ - Breathing: LEDs brightness varies at human breathing rate
+
+ You can also select which leds you want to enable.
+
+ Currently the following devices are know to be supported:
+ - MSI GT683R
+
config HID_HUION
tristate "Huion tablets"
depends on USB_HID
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index fc712dd..7129311 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -48,6 +48,7 @@ obj-$(CONFIG_HID_EMS_FF) += hid-emsff.o
obj-$(CONFIG_HID_ELECOM) += hid-elecom.o
obj-$(CONFIG_HID_ELO) += hid-elo.o
obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o
+obj-$(CONFIG_HID_GT683R) += hid-gt683r.o
obj-$(CONFIG_HID_GYRATION) += hid-gyration.o
obj-$(CONFIG_HID_HOLTEK) += hid-holtek-kbd.o
obj-$(CONFIG_HID_HOLTEK) += hid-holtek-mouse.o
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index da52279..ec88fdb 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1827,6 +1827,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_OFFICE_KB) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) },
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_1) },
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_2) },
diff --git a/drivers/hid/hid-gt683r.c b/drivers/hid/hid-gt683r.c
new file mode 100644
index 0000000..1b16250
--- /dev/null
+++ b/drivers/hid/hid-gt683r.c
@@ -0,0 +1,318 @@
+/*
+ * MSI GT683R led driver
+ *
+ * Copyright (c) 2014 Janne Kanniainen <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+#define GT683R_BUFFER_SIZE 8
+
+/*
+ * GT683R_LED_OFF: all LEDs are off
+ * GT683R_LED_AUDIO: LEDs brightness depends
+ * on sound level
+ * GT683R_LED_BREATHING: LEDs brightness varies
+ * at human breathing rate
+ * GT683R_LED_NORMAL: LEDs are on
+ */
+enum gt683r_led_mode {
+ GT683R_LED_OFF = 0,
+ GT683R_LED_AUDIO = 2,
+ GT683R_LED_BREATHING = 3,
+ GT683R_LED_NORMAL = 5
+};
+
+enum gt683r_panels {
+ GT683R_LED_BACK,
+ GT683R_LED_SIDE,
+ GT683R_LED_FRONT,
+ GT683R_LED_COUNT,
+};
+
+static const char * const gt683r_panel_names[] = {
+ "back",
+ "side",
+ "front",
+};
+
+struct gt683r_led {
+ struct hid_device *hdev;
+ struct led_classdev led_devs[GT683R_LED_COUNT];
+ struct mutex lock;
+ struct work_struct work;
+ enum led_brightness brightnesses[GT683R_LED_COUNT];
+ enum gt683r_led_mode mode;
+};
+
+static const struct hid_device_id gt683r_led_id[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
+ { }
+};
+
+static void gt683r_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ int i;
+ struct device *dev = led_cdev->dev->parent;
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct gt683r_led *led = hid_get_drvdata(hdev);
+
+ for (i = 0; i < GT683R_LED_COUNT; i++) {
+ if (&led->led_devs[i] == led_cdev)
+ break;
+ }
+
+ if (i < GT683R_LED_COUNT) {
+ led->brightnesses[i] = brightness;
+ schedule_work(&led->work);
+ }
+}
+
+static ssize_t mode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ u8 sysfs_mode;
+ struct hid_device *hdev =
+ container_of(dev, struct hid_device, dev);
+ struct gt683r_led *led = hid_get_drvdata(hdev);
+
+ if (led->mode == GT683R_LED_NORMAL)
+ sysfs_mode = 0;
+ else if (led->mode == GT683R_LED_AUDIO)
+ sysfs_mode = 1;
+ else
+ sysfs_mode = 2;
+
+ return scnprintf(buf, PAGE_SIZE, "%u\n", sysfs_mode);
+}
+
+static ssize_t mode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ u8 sysfs_mode;
+ struct hid_device *hdev =
+ container_of(dev, struct hid_device, dev);
+ struct gt683r_led *led = hid_get_drvdata(hdev);
+
+ mutex_lock(&led->lock);
+
+ if (kstrtou8(buf, 10, &sysfs_mode) || sysfs_mode > 2) {
+ count = -EINVAL;
+ goto fail;
+ }
+
+ if (sysfs_mode == 0)
+ led->mode = GT683R_LED_NORMAL;
+ else if (sysfs_mode == 1)
+ led->mode = GT683R_LED_AUDIO;
+ else
+ led->mode = GT683R_LED_BREATHING;
+
+fail:
+ mutex_unlock(&led->lock);
+
+ if (count != -EINVAL)
+ schedule_work(&led->work);
+
+ return count;
+}
+
+static int gt683r_led_snd_msg(struct gt683r_led *led, u8 *msg)
+{
+ int ret;
+
+ ret = hid_hw_raw_request(led->hdev, msg[0], msg, GT683R_BUFFER_SIZE,
+ HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
+ if (ret != GT683R_BUFFER_SIZE) {
+ hid_err(led->hdev,
+ "failed to send set report request: %i\n", ret);
+ if (ret < 0)
+ return ret;
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int gt683r_leds_set(struct gt683r_led *led, u8 leds)
+{
+ int ret;
+ u8 *buffer;
+
+ buffer = kzalloc(GT683R_BUFFER_SIZE, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
+ buffer[0] = 0x01;
+ buffer[1] = 0x02;
+ buffer[2] = 0x30;
+ buffer[3] = leds;
+ ret = gt683r_led_snd_msg(led, buffer);
+
+ kfree(buffer);
+ return ret;
+}
+
+static int gt683r_mode_set(struct gt683r_led *led, u8 mode)
+{
+ int ret;
+ u8 *buffer;
+
+ buffer = kzalloc(GT683R_BUFFER_SIZE, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
+ buffer[0] = 0x01;
+ buffer[1] = 0x02;
+ buffer[2] = 0x20;
+ buffer[3] = mode;
+ buffer[4] = 0x01;
+ ret = gt683r_led_snd_msg(led, buffer);
+
+ kfree(buffer);
+ return ret;
+}
+
+static void gt683r_led_work(struct work_struct *work)
+{
+ u8 leds = 0, mode;
+ struct gt683r_led *led =
+ container_of(work, struct gt683r_led, work);
+
+ mutex_lock(&led->lock);
+
+ if (led->brightnesses[GT683R_LED_BACK])
+ leds |= BIT(GT683R_LED_BACK);
+
+ if (led->brightnesses[GT683R_LED_SIDE])
+ leds |= BIT(GT683R_LED_SIDE);
+
+ if (led->brightnesses[GT683R_LED_FRONT])
+ leds |= BIT(GT683R_LED_FRONT);
+
+ if (gt683r_leds_set(led, leds))
+ goto fail;
+
+ if (leds)
+ mode = led->mode;
+ else
+ mode = GT683R_LED_OFF;
+
+ gt683r_mode_set(led, mode);
+fail:
+ mutex_unlock(&led->lock);
+}
+
+static DEVICE_ATTR_RW(mode);
+
+static int gt683r_led_probe(struct hid_device *hdev,
+ const struct hid_device_id *id)
+{
+ int ret, i, name_sz;
+ char *name;
+ struct gt683r_led *led;
+
+ led = devm_kzalloc(&hdev->dev, sizeof(struct gt683r_led), GFP_KERNEL);
+ if (!led)
+ return -ENOMEM;
+
+ led->mode = GT683R_LED_NORMAL;
+ led->hdev = hdev;
+ hid_set_drvdata(hdev, led);
+
+ ret = hid_parse(hdev);
+ if (ret) {
+ hid_err(hdev, "hid parsing failed\n");
+ goto fail;
+ }
+
+ ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
+ if (ret) {
+ hid_err(hdev, "hw start failed\n");
+ goto fail;
+ }
+
+ for (i = 0; i < GT683R_LED_COUNT; i++) {
+ name_sz = strlen(dev_name(&hdev->dev)) +
+ strlen(gt683r_panel_names[i]) + 3;
+
+ name = devm_kzalloc(&hdev->dev, sizeof(char) * name_sz,
+ GFP_KERNEL);
+
+ snprintf(name, name_sz, "%s::%s",
+ dev_name(&hdev->dev), gt683r_panel_names[i]);
+ led->led_devs[i].name = name;
+ led->led_devs[i].max_brightness = 1;
+ led->led_devs[i].brightness_set = gt683r_brightness_set;
+ ret = led_classdev_register(&hdev->dev, &led->led_devs[i]);
+ if (ret) {
+ hid_err(hdev, "could not register led device\n");
+ goto fail2;
+ }
+ }
+
+ ret = device_create_file(&led->hdev->dev,
+ &dev_attr_mode);
+ if (ret) {
+ hid_err(hdev, "could not make mode attribute file\n");
+ goto fail2;
+ }
+
+ mutex_init(&led->lock);
+ INIT_WORK(&led->work, gt683r_led_work);
+
+ return 0;
+
+fail2:
+ for (; i >= 0; i--)
+ led_classdev_unregister(&led->led_devs[i]);
+ hid_hw_stop(hdev);
+fail:
+ return ret;
+}
+
+static void gt683r_led_remove(struct hid_device *hdev)
+{
+ int i;
+ struct gt683r_led *led = hid_get_drvdata(hdev);
+
+ for (i = 0; i < GT683R_LED_COUNT; i++)
+ led_classdev_unregister(&led->led_devs[i]);
+ device_remove_file(&hdev->dev,
+ &dev_attr_mode);
+ flush_work(&led->work);
+ hid_hw_stop(hdev);
+}
+
+static struct hid_driver gt683r_led_driver = {
+ .probe = gt683r_led_probe,
+ .remove = gt683r_led_remove,
+ .name = "gt683r_led",
+ .id_table = gt683r_led_id,
+};
+
+module_hid_driver(gt683r_led_driver);
+
+MODULE_AUTHOR("Janne Kanniainen");
+MODULE_DESCRIPTION("MSI GT683R led driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 34bb220..3692d37 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -641,7 +641,7 @@
#define USB_DEVICE_ID_GENIUS_KB29E 0x3004

#define USB_VENDOR_ID_MSI 0x1770
-#define USB_DEVICE_ID_MSI_GX680R_LED_PANEL 0xff00
+#define USB_DEVICE_ID_MSI_GT683R_LED_PANEL 0xff00

#define USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR 0x0400
#define USB_DEVICE_ID_N_S_HARMONY 0xc359
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
index 8e4ddb3..c640e1d 100644
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -73,7 +73,7 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET },
- { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GX680R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
+ { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_NEXIO, USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_MOUSE, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NO_INIT_REPORTS },
--
1.9.2

2014-06-17 13:46:45

by Johan Hovold

[permalink] [raw]
Subject: Re: [PATCH v6] leds: USB: HID: Add support for MSI GT683R led panels

On Tue, Jun 17, 2014 at 01:01:55AM +0300, Janne Kanniainen wrote:
> This driver adds support for USB controlled led panels that exists in
> MSI GT683R laptop
>
> Signed-off-by: Janne Kanniainen <[email protected]>
> ---
> Changes in v2:
> - sorted headers to alphabetic order
> - using devm_kzalloc
> - using BIT(n)
> - using usb_control_msg instead of usb_submit_urb
> - removing unneeded code
>
> Changes in v3:
> - implemented as HID device
> - some cleanups and bug fixes
>
> Changes in v4:
> - more cleanups
> - support for selecting leds
> - suppport for selecting status
>
> Changes in v5:
> - mode attribute documented under Documentation/ABI
> - made array for led_classdev
> - led devices uses now recommended naming scheme
>
> Changes in v6:
> - flush_work added
> - using hid device name instead of hard coded gt683r
> - allocating name buffers with devm_kzalloc
>
> There was a bug with "for", so I fixed it.

Then it really was a v7 and should have been marked as such, right?

> .../ABI/testing/sysfs-class-hid-driver-gt683r | 14 +
> drivers/hid/Kconfig | 16 ++
> drivers/hid/Makefile | 1 +
> drivers/hid/hid-core.c | 1 +
> drivers/hid/hid-gt683r.c | 318 +++++++++++++++++++++
> drivers/hid/hid-ids.h | 2 +-
> drivers/hid/usbhid/hid-quirks.c | 2 +-
> 7 files changed, 352 insertions(+), 2 deletions(-)
> create mode 100644 Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
> create mode 100644 drivers/hid/hid-gt683r.c
>
> diff --git a/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
> new file mode 100644
> index 0000000..6d0bd80
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
> @@ -0,0 +1,14 @@
> +What: /sys/class/hidraw/<hidraw>/device/mode

Perhaps you should name this "leds_mode" or "led_panel_mode", or similar
(only change the attribute name, code is fine otherwise). This is an
attribute of the parent HID device, but it's not really apparent what
just "mode" would mean for such a device.

> +Date: Jun 2014
> +KernelVersion: 3.17
> +Contact: Janne Kanniainen <[email protected]>
> +Description:
> + Set the mode of LEDs
> +
> + 0 - normal
> + 1 - audio
> + 2 - breathing
> +
> + Normal: LEDs are on

Change this to "LEDs are fully on when enabled", or similar?

> + Audio: LEDs brightness depends on sound level
> + Breathing: LEDs brightness varies at human breathing rate
> \ No newline at end of file
> diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
> index 7af9d0b..b88cabd 100644
> --- a/drivers/hid/Kconfig
> +++ b/drivers/hid/Kconfig
> @@ -261,6 +261,22 @@ config HOLTEK_FF
> Say Y here if you have a Holtek On Line Grip based game controller
> and want to have force feedback support for it.
>
> +config HID_GT683R
> + tristate "MSI GT68xR LED support"
> + depends on LEDS_CLASS && USB_HID
> + ---help---
> + Say Y here if you want to enable support for the MSI GT68xR LEDS
> +
> + This driver support following modes:
> + - Normal: LEDs are on
> + - Audio: LEDs brightness depends on sound level
> + - Breathing: LEDs brightness varies at human breathing rate

This might need an update as well.

> +
> + You can also select which leds you want to enable.

You could drop this line, and just say "enable support for the three
MSI" LEDs above?

> +
> + Currently the following devices are know to be supported:
> + - MSI GT683R
> +
> config HID_HUION
> tristate "Huion tablets"
> depends on USB_HID
> diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
> index fc712dd..7129311 100644
> --- a/drivers/hid/Makefile
> +++ b/drivers/hid/Makefile
> @@ -48,6 +48,7 @@ obj-$(CONFIG_HID_EMS_FF) += hid-emsff.o
> obj-$(CONFIG_HID_ELECOM) += hid-elecom.o
> obj-$(CONFIG_HID_ELO) += hid-elo.o
> obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o
> +obj-$(CONFIG_HID_GT683R) += hid-gt683r.o
> obj-$(CONFIG_HID_GYRATION) += hid-gyration.o
> obj-$(CONFIG_HID_HOLTEK) += hid-holtek-kbd.o
> obj-$(CONFIG_HID_HOLTEK) += hid-holtek-mouse.o
> diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
> index da52279..ec88fdb 100644
> --- a/drivers/hid/hid-core.c
> +++ b/drivers/hid/hid-core.c
> @@ -1827,6 +1827,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
> { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) },
> { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_OFFICE_KB) },
> { HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) },
> + { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
> { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) },
> { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_1) },
> { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_2) },
> diff --git a/drivers/hid/hid-gt683r.c b/drivers/hid/hid-gt683r.c
> new file mode 100644
> index 0000000..1b16250
> --- /dev/null
> +++ b/drivers/hid/hid-gt683r.c
> @@ -0,0 +1,318 @@
> +/*
> + * MSI GT683R led driver
> + *
> + * Copyright (c) 2014 Janne Kanniainen <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/device.h>
> +#include <linux/hid.h>
> +#include <linux/kernel.h>
> +#include <linux/leds.h>
> +#include <linux/module.h>
> +
> +#include "hid-ids.h"
> +
> +#define GT683R_BUFFER_SIZE 8
> +
> +/*
> + * GT683R_LED_OFF: all LEDs are off
> + * GT683R_LED_AUDIO: LEDs brightness depends
> + * on sound level
> + * GT683R_LED_BREATHING: LEDs brightness varies
> + * at human breathing rate

No need to break these two lines (they would still be below 80 cols
wide).

> + * GT683R_LED_NORMAL: LEDs are on
> + */
> +enum gt683r_led_mode {
> + GT683R_LED_OFF = 0,
> + GT683R_LED_AUDIO = 2,
> + GT683R_LED_BREATHING = 3,
> + GT683R_LED_NORMAL = 5
> +};
> +
> +enum gt683r_panels {
> + GT683R_LED_BACK,
> + GT683R_LED_SIDE,
> + GT683R_LED_FRONT,

Perhaps initialise these three explicitly as the order really isn't
arbitrary?

> + GT683R_LED_COUNT,
> +};
> +
> +static const char * const gt683r_panel_names[] = {
> + "back",
> + "side",
> + "front",
> +};
> +
> +struct gt683r_led {
> + struct hid_device *hdev;
> + struct led_classdev led_devs[GT683R_LED_COUNT];
> + struct mutex lock;
> + struct work_struct work;
> + enum led_brightness brightnesses[GT683R_LED_COUNT];
> + enum gt683r_led_mode mode;
> +};
> +
> +static const struct hid_device_id gt683r_led_id[] = {
> + { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
> + { }
> +};
> +
> +static void gt683r_brightness_set(struct led_classdev *led_cdev,
> + enum led_brightness brightness)
> +{
> + int i;
> + struct device *dev = led_cdev->dev->parent;
> + struct hid_device *hdev = container_of(dev, struct hid_device, dev);
> + struct gt683r_led *led = hid_get_drvdata(hdev);
> +
> + for (i = 0; i < GT683R_LED_COUNT; i++) {
> + if (&led->led_devs[i] == led_cdev)
> + break;
> + }
> +
> + if (i < GT683R_LED_COUNT) {
> + led->brightnesses[i] = brightness;
> + schedule_work(&led->work);
> + }
> +}
> +
> +static ssize_t mode_show(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> +{
> + u8 sysfs_mode;
> + struct hid_device *hdev =
> + container_of(dev, struct hid_device, dev);

No need to break this (still < 80 cols).

> + struct gt683r_led *led = hid_get_drvdata(hdev);
> +
> + if (led->mode == GT683R_LED_NORMAL)
> + sysfs_mode = 0;
> + else if (led->mode == GT683R_LED_AUDIO)
> + sysfs_mode = 1;
> + else
> + sysfs_mode = 2;
> +
> + return scnprintf(buf, PAGE_SIZE, "%u\n", sysfs_mode);
> +}
> +
> +static ssize_t mode_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + u8 sysfs_mode;
> + struct hid_device *hdev =
> + container_of(dev, struct hid_device, dev);

No need to break this (still < 80 cols).

> + struct gt683r_led *led = hid_get_drvdata(hdev);
> +
> + mutex_lock(&led->lock);
> +
> + if (kstrtou8(buf, 10, &sysfs_mode) || sysfs_mode > 2) {
> + count = -EINVAL;
> + goto fail;
> + }
> +
> + if (sysfs_mode == 0)
> + led->mode = GT683R_LED_NORMAL;
> + else if (sysfs_mode == 1)
> + led->mode = GT683R_LED_AUDIO;
> + else
> + led->mode = GT683R_LED_BREATHING;
> +
> +fail:
> + mutex_unlock(&led->lock);
> +
> + if (count != -EINVAL)
> + schedule_work(&led->work);
> +
> + return count;
> +}

This could be simplified somewhat as the lock only needs to protect the
assignment to led->mode.

(Glad to see you realised I meant kstrouX when I wrote snprintf. ;))

> +
> +static int gt683r_led_snd_msg(struct gt683r_led *led, u8 *msg)
> +{
> + int ret;
> +
> + ret = hid_hw_raw_request(led->hdev, msg[0], msg, GT683R_BUFFER_SIZE,
> + HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
> + if (ret != GT683R_BUFFER_SIZE) {
> + hid_err(led->hdev,
> + "failed to send set report request: %i\n", ret);
> + if (ret < 0)
> + return ret;
> + return -EIO;
> + }
> +
> + return 0;
> +}
> +
> +static int gt683r_leds_set(struct gt683r_led *led, u8 leds)
> +{
> + int ret;
> + u8 *buffer;
> +
> + buffer = kzalloc(GT683R_BUFFER_SIZE, GFP_KERNEL);
> + if (!buffer)
> + return -ENOMEM;
> +
> + buffer[0] = 0x01;
> + buffer[1] = 0x02;
> + buffer[2] = 0x30;
> + buffer[3] = leds;
> + ret = gt683r_led_snd_msg(led, buffer);
> +
> + kfree(buffer);
> + return ret;
> +}
> +
> +static int gt683r_mode_set(struct gt683r_led *led, u8 mode)
> +{
> + int ret;
> + u8 *buffer;
> +
> + buffer = kzalloc(GT683R_BUFFER_SIZE, GFP_KERNEL);
> + if (!buffer)
> + return -ENOMEM;
> +
> + buffer[0] = 0x01;
> + buffer[1] = 0x02;
> + buffer[2] = 0x20;
> + buffer[3] = mode;
> + buffer[4] = 0x01;
> + ret = gt683r_led_snd_msg(led, buffer);
> +
> + kfree(buffer);
> + return ret;
> +}
> +
> +static void gt683r_led_work(struct work_struct *work)
> +{
> + u8 leds = 0, mode;

Try to stick to one definition per line (throughout), especially if
you're initialising as well.

> + struct gt683r_led *led =
> + container_of(work, struct gt683r_led, work);
> +
> + mutex_lock(&led->lock);
> +
> + if (led->brightnesses[GT683R_LED_BACK])
> + leds |= BIT(GT683R_LED_BACK);
> +
> + if (led->brightnesses[GT683R_LED_SIDE])
> + leds |= BIT(GT683R_LED_SIDE);
> +
> + if (led->brightnesses[GT683R_LED_FRONT])
> + leds |= BIT(GT683R_LED_FRONT);

This could of course be implemented as a loop as well, if you want...

> +
> + if (gt683r_leds_set(led, leds))
> + goto fail;
> +
> + if (leds)
> + mode = led->mode;
> + else
> + mode = GT683R_LED_OFF;
> +
> + gt683r_mode_set(led, mode);
> +fail:
> + mutex_unlock(&led->lock);
> +}
> +
> +static DEVICE_ATTR_RW(mode);
> +
> +static int gt683r_led_probe(struct hid_device *hdev,
> + const struct hid_device_id *id)
> +{
> + int ret, i, name_sz;
> + char *name;
> + struct gt683r_led *led;
> +
> + led = devm_kzalloc(&hdev->dev, sizeof(struct gt683r_led), GFP_KERNEL);
> + if (!led)
> + return -ENOMEM;
> +
> + led->mode = GT683R_LED_NORMAL;
> + led->hdev = hdev;
> + hid_set_drvdata(hdev, led);
> +
> + ret = hid_parse(hdev);
> + if (ret) {
> + hid_err(hdev, "hid parsing failed\n");
> + goto fail;

Just return ret here.

> + }
> +
> + ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
> + if (ret) {
> + hid_err(hdev, "hw start failed\n");
> + goto fail;
> + }
> +
> + for (i = 0; i < GT683R_LED_COUNT; i++) {
> + name_sz = strlen(dev_name(&hdev->dev)) +
> + strlen(gt683r_panel_names[i]) + 3;
> +
> + name = devm_kzalloc(&hdev->dev, sizeof(char) * name_sz,
> + GFP_KERNEL);

Must check for allocation failure still.

> +
> + snprintf(name, name_sz, "%s::%s",
> + dev_name(&hdev->dev), gt683r_panel_names[i]);
> + led->led_devs[i].name = name;
> + led->led_devs[i].max_brightness = 1;
> + led->led_devs[i].brightness_set = gt683r_brightness_set;
> + ret = led_classdev_register(&hdev->dev, &led->led_devs[i]);
> + if (ret) {
> + hid_err(hdev, "could not register led device\n");
> + goto fail2;
> + }
> + }
> +
> + ret = device_create_file(&led->hdev->dev,
> + &dev_attr_mode);

No need to break line.

> + if (ret) {
> + hid_err(hdev, "could not make mode attribute file\n");
> + goto fail2;
> + }
> +
> + mutex_init(&led->lock);
> + INIT_WORK(&led->work, gt683r_led_work);
> +
> + return 0;
> +
> +fail2:
> + for (; i >= 0; i--)

You must initialise i to (i - 1) as well (either led_class_devregister
failed or i == GT683R_LED_COUNT here).

> + led_classdev_unregister(&led->led_devs[i]);
> + hid_hw_stop(hdev);
> +fail:
> + return ret;
> +}
> +
> +static void gt683r_led_remove(struct hid_device *hdev)
> +{
> + int i;
> + struct gt683r_led *led = hid_get_drvdata(hdev);
> +
> + for (i = 0; i < GT683R_LED_COUNT; i++)
> + led_classdev_unregister(&led->led_devs[i]);
> + device_remove_file(&hdev->dev,
> + &dev_attr_mode);

No need to break line.

Perhaps do the removal before unregistering to reverse probe.

> + flush_work(&led->work);
> + hid_hw_stop(hdev);
> +}
> +
> +static struct hid_driver gt683r_led_driver = {
> + .probe = gt683r_led_probe,
> + .remove = gt683r_led_remove,
> + .name = "gt683r_led",
> + .id_table = gt683r_led_id,
> +};
> +
> +module_hid_driver(gt683r_led_driver);
> +
> +MODULE_AUTHOR("Janne Kanniainen");
> +MODULE_DESCRIPTION("MSI GT683R led driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
> index 34bb220..3692d37 100644
> --- a/drivers/hid/hid-ids.h
> +++ b/drivers/hid/hid-ids.h
> @@ -641,7 +641,7 @@
> #define USB_DEVICE_ID_GENIUS_KB29E 0x3004
>
> #define USB_VENDOR_ID_MSI 0x1770
> -#define USB_DEVICE_ID_MSI_GX680R_LED_PANEL 0xff00
> +#define USB_DEVICE_ID_MSI_GT683R_LED_PANEL 0xff00
>
> #define USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR 0x0400
> #define USB_DEVICE_ID_N_S_HARMONY 0xc359
> diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
> index 8e4ddb3..c640e1d 100644
> --- a/drivers/hid/usbhid/hid-quirks.c
> +++ b/drivers/hid/usbhid/hid-quirks.c
> @@ -73,7 +73,7 @@ static const struct hid_blacklist {
> { USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS },
> { USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET },
> { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET },
> - { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GX680R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
> + { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
> { USB_VENDOR_ID_NEXIO, USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750, HID_QUIRK_NO_INIT_REPORTS },
> { USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_MOUSE, HID_QUIRK_NO_INIT_REPORTS },
> { USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NO_INIT_REPORTS },

Johan

2014-06-17 16:42:06

by Janne Kanniainen

[permalink] [raw]
Subject: [PATCH v8] leds: USB: HID: Add support for MSI GT683R led panels

This driver adds support for USB controlled led panels that exists in
MSI GT683R laptop

Changes in v2:
- sorted headers to alphabetic order
- using devm_kzalloc
- using BIT(n)
- using usb_control_msg instead of usb_submit_urb
- removing unneeded code
Changes in v3:
- implemented as HID device
- some cleanups and bug fixes

Changes in v4:
- more cleanups
- support for selecting leds
- suppport for selecting status

Changes in v5:
- mode attribute documented under Documentation/ABI
- made array for led_classdev
- led devices uses now recommended naming scheme

Changes in v6:
- flush_work added
- using hid device name instead of hard coded gt683r
- allocating name buffers with devm_kzalloc

Changes in v7:
- buf with for fixed

Changes in v8:
- some cleanups and bugs fixed

Signed-off-by: Janne Kanniainen <[email protected]>
---
.../ABI/testing/sysfs-class-hid-driver-gt683r | 14 +
drivers/hid/Kconfig | 14 +
drivers/hid/Makefile | 1 +
drivers/hid/hid-core.c | 1 +
drivers/hid/hid-gt683r.c | 308 +++++++++++++++++++++
drivers/hid/hid-ids.h | 2 +-
drivers/hid/usbhid/hid-quirks.c | 2 +-
7 files changed, 340 insertions(+), 2 deletions(-)
create mode 100644 Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
create mode 100644 drivers/hid/hid-gt683r.c

diff --git a/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
new file mode 100644
index 0000000..317e9d5
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
@@ -0,0 +1,14 @@
+What: /sys/class/hidraw/<hidraw>/device/leds_mode
+Date: Jun 2014
+KernelVersion: 3.17
+Contact: Janne Kanniainen <[email protected]>
+Description:
+ Set the mode of LEDs
+
+ 0 - normal
+ 1 - audio
+ 2 - breathing
+
+ Normal: LEDs are fully on when enabled
+ Audio: LEDs brightness depends on sound level
+ Breathing: LEDs brightness varies at human breathing rate
\ No newline at end of file
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 7af9d0b..e2f4590 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -261,6 +261,20 @@ config HOLTEK_FF
Say Y here if you have a Holtek On Line Grip based game controller
and want to have force feedback support for it.

+config HID_GT683R
+ tristate "MSI GT68xR LED support"
+ depends on LEDS_CLASS && USB_HID
+ ---help---
+ Say Y here if you want to enable support for the three MSI GT68xR LEDs
+
+ This driver support following modes:
+ - Normal: LEDs are fully on when enabled
+ - Audio: LEDs brightness depends on sound level
+ - Breathing: LEDs brightness varies at human breathing rate
+
+ Currently the following devices are know to be supported:
+ - MSI GT683R
+
config HID_HUION
tristate "Huion tablets"
depends on USB_HID
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index fc712dd..7129311 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -48,6 +48,7 @@ obj-$(CONFIG_HID_EMS_FF) += hid-emsff.o
obj-$(CONFIG_HID_ELECOM) += hid-elecom.o
obj-$(CONFIG_HID_ELO) += hid-elo.o
obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o
+obj-$(CONFIG_HID_GT683R) += hid-gt683r.o
obj-$(CONFIG_HID_GYRATION) += hid-gyration.o
obj-$(CONFIG_HID_HOLTEK) += hid-holtek-kbd.o
obj-$(CONFIG_HID_HOLTEK) += hid-holtek-mouse.o
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index da52279..ec88fdb 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1827,6 +1827,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_OFFICE_KB) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) },
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_1) },
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_2) },
diff --git a/drivers/hid/hid-gt683r.c b/drivers/hid/hid-gt683r.c
new file mode 100644
index 0000000..19959ac
--- /dev/null
+++ b/drivers/hid/hid-gt683r.c
@@ -0,0 +1,308 @@
+/*
+ * MSI GT683R led driver
+ *
+ * Copyright (c) 2014 Janne Kanniainen <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+#define GT683R_BUFFER_SIZE 8
+
+/*
+ * GT683R_LED_OFF: all LEDs are off
+ * GT683R_LED_AUDIO: LEDs brightness depends on sound level
+ * GT683R_LED_BREATHING: LEDs brightness varies at human breathing rate
+ * GT683R_LED_NORMAL: LEDs are fully on when enabled
+ */
+enum gt683r_led_mode {
+ GT683R_LED_OFF = 0,
+ GT683R_LED_AUDIO = 2,
+ GT683R_LED_BREATHING = 3,
+ GT683R_LED_NORMAL = 5
+};
+
+enum gt683r_panels {
+ GT683R_LED_BACK = 0,
+ GT683R_LED_SIDE = 1,
+ GT683R_LED_FRONT = 2,
+ GT683R_LED_COUNT,
+};
+
+static const char * const gt683r_panel_names[] = {
+ "back",
+ "side",
+ "front",
+};
+
+struct gt683r_led {
+ struct hid_device *hdev;
+ struct led_classdev led_devs[GT683R_LED_COUNT];
+ struct mutex lock;
+ struct work_struct work;
+ enum led_brightness brightnesses[GT683R_LED_COUNT];
+ enum gt683r_led_mode mode;
+};
+
+static const struct hid_device_id gt683r_led_id[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
+ { }
+};
+
+static void gt683r_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ int i;
+ struct device *dev = led_cdev->dev->parent;
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct gt683r_led *led = hid_get_drvdata(hdev);
+
+ for (i = 0; i < GT683R_LED_COUNT; i++) {
+ if (&led->led_devs[i] == led_cdev)
+ break;
+ }
+
+ if (i < GT683R_LED_COUNT) {
+ led->brightnesses[i] = brightness;
+ schedule_work(&led->work);
+ }
+}
+
+static ssize_t leds_mode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ u8 sysfs_mode;
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct gt683r_led *led = hid_get_drvdata(hdev);
+
+ if (led->mode == GT683R_LED_NORMAL)
+ sysfs_mode = 0;
+ else if (led->mode == GT683R_LED_AUDIO)
+ sysfs_mode = 1;
+ else
+ sysfs_mode = 2;
+
+ return scnprintf(buf, PAGE_SIZE, "%u\n", sysfs_mode);
+}
+
+static ssize_t leds_mode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ u8 sysfs_mode;
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct gt683r_led *led = hid_get_drvdata(hdev);
+
+
+ if (kstrtou8(buf, 10, &sysfs_mode) || sysfs_mode > 2)
+ return -EINVAL;
+
+ mutex_lock(&led->lock);
+
+ if (sysfs_mode == 0)
+ led->mode = GT683R_LED_NORMAL;
+ else if (sysfs_mode == 1)
+ led->mode = GT683R_LED_AUDIO;
+ else
+ led->mode = GT683R_LED_BREATHING;
+
+ mutex_unlock(&led->lock);
+ schedule_work(&led->work);
+
+ return count;
+}
+
+static int gt683r_led_snd_msg(struct gt683r_led *led, u8 *msg)
+{
+ int ret;
+
+ ret = hid_hw_raw_request(led->hdev, msg[0], msg, GT683R_BUFFER_SIZE,
+ HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
+ if (ret != GT683R_BUFFER_SIZE) {
+ hid_err(led->hdev,
+ "failed to send set report request: %i\n", ret);
+ if (ret < 0)
+ return ret;
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int gt683r_leds_set(struct gt683r_led *led, u8 leds)
+{
+ int ret;
+ u8 *buffer;
+
+ buffer = kzalloc(GT683R_BUFFER_SIZE, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
+ buffer[0] = 0x01;
+ buffer[1] = 0x02;
+ buffer[2] = 0x30;
+ buffer[3] = leds;
+ ret = gt683r_led_snd_msg(led, buffer);
+
+ kfree(buffer);
+ return ret;
+}
+
+static int gt683r_mode_set(struct gt683r_led *led, u8 mode)
+{
+ int ret;
+ u8 *buffer;
+
+ buffer = kzalloc(GT683R_BUFFER_SIZE, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
+ buffer[0] = 0x01;
+ buffer[1] = 0x02;
+ buffer[2] = 0x20;
+ buffer[3] = mode;
+ buffer[4] = 0x01;
+ ret = gt683r_led_snd_msg(led, buffer);
+
+ kfree(buffer);
+ return ret;
+}
+
+static void gt683r_led_work(struct work_struct *work)
+{
+ int i;
+ u8 leds = 0;
+ u8 mode;
+ struct gt683r_led *led = container_of(work, struct gt683r_led, work);
+
+ mutex_lock(&led->lock);
+
+ for (i = 0; i < GT683R_LED_COUNT; i++) {
+ if (led->brightnesses[i])
+ leds |= BIT(i);
+ }
+
+ if (gt683r_leds_set(led, leds))
+ goto fail;
+
+ if (leds)
+ mode = led->mode;
+ else
+ mode = GT683R_LED_OFF;
+
+ gt683r_mode_set(led, mode);
+fail:
+ mutex_unlock(&led->lock);
+}
+
+static DEVICE_ATTR_RW(leds_mode);
+
+static int gt683r_led_probe(struct hid_device *hdev,
+ const struct hid_device_id *id)
+{
+ int i, ret, name_sz;
+ char *name;
+ struct gt683r_led *led;
+
+ led = devm_kzalloc(&hdev->dev, sizeof(struct gt683r_led), GFP_KERNEL);
+ if (!led)
+ return -ENOMEM;
+
+ led->mode = GT683R_LED_NORMAL;
+ led->hdev = hdev;
+ hid_set_drvdata(hdev, led);
+
+ ret = hid_parse(hdev);
+ if (ret) {
+ hid_err(hdev, "hid parsing failed\n");
+ return ret;
+ }
+
+ ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
+ if (ret) {
+ hid_err(hdev, "hw start failed\n");
+ return ret;
+ }
+
+ for (i = 0; i < GT683R_LED_COUNT; i++) {
+ name_sz = strlen(dev_name(&hdev->dev)) +
+ strlen(gt683r_panel_names[i]) + 3;
+
+ name = devm_kzalloc(&hdev->dev, sizeof(char) * name_sz,
+ GFP_KERNEL);
+ if (!name) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ snprintf(name, name_sz, "%s::%s",
+ dev_name(&hdev->dev), gt683r_panel_names[i]);
+ led->led_devs[i].name = name;
+ led->led_devs[i].max_brightness = 1;
+ led->led_devs[i].brightness_set = gt683r_brightness_set;
+ ret = led_classdev_register(&hdev->dev, &led->led_devs[i]);
+ if (ret) {
+ hid_err(hdev, "could not register led device\n");
+ goto fail;
+ }
+ }
+
+ ret = device_create_file(&led->hdev->dev, &dev_attr_leds_mode);
+ if (ret) {
+ hid_err(hdev, "could not make mode attribute file\n");
+ goto fail;
+ }
+
+ mutex_init(&led->lock);
+ INIT_WORK(&led->work, gt683r_led_work);
+
+ return 0;
+
+fail:
+ for (i = i - 1; i >= 0; i--)
+ led_classdev_unregister(&led->led_devs[i]);
+ hid_hw_stop(hdev);
+ return ret;
+}
+
+static void gt683r_led_remove(struct hid_device *hdev)
+{
+ int i;
+ struct gt683r_led *led = hid_get_drvdata(hdev);
+
+ device_remove_file(&hdev->dev, &dev_attr_leds_mode);
+ for (i = 0; i < GT683R_LED_COUNT; i++)
+ led_classdev_unregister(&led->led_devs[i]);
+ flush_work(&led->work);
+ hid_hw_stop(hdev);
+}
+
+static struct hid_driver gt683r_led_driver = {
+ .probe = gt683r_led_probe,
+ .remove = gt683r_led_remove,
+ .name = "gt683r_led",
+ .id_table = gt683r_led_id,
+};
+
+module_hid_driver(gt683r_led_driver);
+
+MODULE_AUTHOR("Janne Kanniainen");
+MODULE_DESCRIPTION("MSI GT683R led driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 34bb220..3692d37 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -641,7 +641,7 @@
#define USB_DEVICE_ID_GENIUS_KB29E 0x3004

#define USB_VENDOR_ID_MSI 0x1770
-#define USB_DEVICE_ID_MSI_GX680R_LED_PANEL 0xff00
+#define USB_DEVICE_ID_MSI_GT683R_LED_PANEL 0xff00

#define USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR 0x0400
#define USB_DEVICE_ID_N_S_HARMONY 0xc359
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
index 8e4ddb3..c640e1d 100644
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -73,7 +73,7 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET },
- { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GX680R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
+ { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_NEXIO, USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_MOUSE, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NO_INIT_REPORTS },
--
1.9.2

2014-06-18 07:40:33

by Johan Hovold

[permalink] [raw]
Subject: Re: [PATCH v8] leds: USB: HID: Add support for MSI GT683R led panels

On Tue, Jun 17, 2014 at 07:41:44PM +0300, Janne Kanniainen wrote:
> This driver adds support for USB controlled led panels that exists in
> MSI GT683R laptop
>
> Changes in v2:
> - sorted headers to alphabetic order
> - using devm_kzalloc
> - using BIT(n)
> - using usb_control_msg instead of usb_submit_urb
> - removing unneeded code
> Changes in v3:
> - implemented as HID device
> - some cleanups and bug fixes
>
> Changes in v4:
> - more cleanups
> - support for selecting leds
> - suppport for selecting status
>
> Changes in v5:
> - mode attribute documented under Documentation/ABI
> - made array for led_classdev
> - led devices uses now recommended naming scheme
>
> Changes in v6:
> - flush_work added
> - using hid device name instead of hard coded gt683r
> - allocating name buffers with devm_kzalloc
>
> Changes in v7:
> - buf with for fixed
>
> Changes in v8:
> - some cleanups and bugs fixed

Great job, Janne. I have a few really minor style issues I just noted
and that I point out below, but that's it.

> Signed-off-by: Janne Kanniainen <[email protected]>
> ---
> .../ABI/testing/sysfs-class-hid-driver-gt683r | 14 +
> drivers/hid/Kconfig | 14 +
> drivers/hid/Makefile | 1 +
> drivers/hid/hid-core.c | 1 +
> drivers/hid/hid-gt683r.c | 308 +++++++++++++++++++++
> drivers/hid/hid-ids.h | 2 +-
> drivers/hid/usbhid/hid-quirks.c | 2 +-
> 7 files changed, 340 insertions(+), 2 deletions(-)
> create mode 100644 Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
> create mode 100644 drivers/hid/hid-gt683r.c
>
> diff --git a/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
> new file mode 100644
> index 0000000..317e9d5
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
> @@ -0,0 +1,14 @@
> +What: /sys/class/hidraw/<hidraw>/device/leds_mode
> +Date: Jun 2014
> +KernelVersion: 3.17
> +Contact: Janne Kanniainen <[email protected]>
> +Description:
> + Set the mode of LEDs
> +
> + 0 - normal
> + 1 - audio
> + 2 - breathing
> +
> + Normal: LEDs are fully on when enabled
> + Audio: LEDs brightness depends on sound level
> + Breathing: LEDs brightness varies at human breathing rate
> \ No newline at end of file
> diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
> index 7af9d0b..e2f4590 100644
> --- a/drivers/hid/Kconfig
> +++ b/drivers/hid/Kconfig
> @@ -261,6 +261,20 @@ config HOLTEK_FF
> Say Y here if you have a Holtek On Line Grip based game controller
> and want to have force feedback support for it.
>
> +config HID_GT683R
> + tristate "MSI GT68xR LED support"
> + depends on LEDS_CLASS && USB_HID
> + ---help---
> + Say Y here if you want to enable support for the three MSI GT68xR LEDs
> +
> + This driver support following modes:
> + - Normal: LEDs are fully on when enabled
> + - Audio: LEDs brightness depends on sound level
> + - Breathing: LEDs brightness varies at human breathing rate
> +
> + Currently the following devices are know to be supported:
> + - MSI GT683R
> +
> config HID_HUION
> tristate "Huion tablets"
> depends on USB_HID
> diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
> index fc712dd..7129311 100644
> --- a/drivers/hid/Makefile
> +++ b/drivers/hid/Makefile
> @@ -48,6 +48,7 @@ obj-$(CONFIG_HID_EMS_FF) += hid-emsff.o
> obj-$(CONFIG_HID_ELECOM) += hid-elecom.o
> obj-$(CONFIG_HID_ELO) += hid-elo.o
> obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o
> +obj-$(CONFIG_HID_GT683R) += hid-gt683r.o
> obj-$(CONFIG_HID_GYRATION) += hid-gyration.o
> obj-$(CONFIG_HID_HOLTEK) += hid-holtek-kbd.o
> obj-$(CONFIG_HID_HOLTEK) += hid-holtek-mouse.o
> diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
> index da52279..ec88fdb 100644
> --- a/drivers/hid/hid-core.c
> +++ b/drivers/hid/hid-core.c
> @@ -1827,6 +1827,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
> { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) },
> { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_OFFICE_KB) },
> { HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) },
> + { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
> { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) },
> { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_1) },
> { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_2) },
> diff --git a/drivers/hid/hid-gt683r.c b/drivers/hid/hid-gt683r.c
> new file mode 100644
> index 0000000..19959ac
> --- /dev/null
> +++ b/drivers/hid/hid-gt683r.c
> @@ -0,0 +1,308 @@
> +/*
> + * MSI GT683R led driver
> + *
> + * Copyright (c) 2014 Janne Kanniainen <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/device.h>
> +#include <linux/hid.h>
> +#include <linux/kernel.h>
> +#include <linux/leds.h>
> +#include <linux/module.h>
> +
> +#include "hid-ids.h"
> +
> +#define GT683R_BUFFER_SIZE 8
> +
> +/*
> + * GT683R_LED_OFF: all LEDs are off
> + * GT683R_LED_AUDIO: LEDs brightness depends on sound level
> + * GT683R_LED_BREATHING: LEDs brightness varies at human breathing rate
> + * GT683R_LED_NORMAL: LEDs are fully on when enabled
> + */
> +enum gt683r_led_mode {
> + GT683R_LED_OFF = 0,
> + GT683R_LED_AUDIO = 2,
> + GT683R_LED_BREATHING = 3,
> + GT683R_LED_NORMAL = 5
> +};
> +
> +enum gt683r_panels {
> + GT683R_LED_BACK = 0,
> + GT683R_LED_SIDE = 1,
> + GT683R_LED_FRONT = 2,
> + GT683R_LED_COUNT,
> +};
> +
> +static const char * const gt683r_panel_names[] = {
> + "back",
> + "side",
> + "front",
> +};
> +
> +struct gt683r_led {
> + struct hid_device *hdev;
> + struct led_classdev led_devs[GT683R_LED_COUNT];
> + struct mutex lock;
> + struct work_struct work;
> + enum led_brightness brightnesses[GT683R_LED_COUNT];
> + enum gt683r_led_mode mode;
> +};
> +
> +static const struct hid_device_id gt683r_led_id[] = {
> + { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
> + { }
> +};
> +
> +static void gt683r_brightness_set(struct led_classdev *led_cdev,
> + enum led_brightness brightness)
> +{
> + int i;
> + struct device *dev = led_cdev->dev->parent;
> + struct hid_device *hdev = container_of(dev, struct hid_device, dev);
> + struct gt683r_led *led = hid_get_drvdata(hdev);
> +
> + for (i = 0; i < GT683R_LED_COUNT; i++) {
> + if (&led->led_devs[i] == led_cdev)

I'd prefer to have led_cdev on the left here comparing against the
"constant" structs on the right.

> + break;
> + }
> +
> + if (i < GT683R_LED_COUNT) {
> + led->brightnesses[i] = brightness;
> + schedule_work(&led->work);
> + }
> +}
> +
> +static ssize_t leds_mode_show(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> +{
> + u8 sysfs_mode;
> + struct hid_device *hdev = container_of(dev, struct hid_device, dev);
> + struct gt683r_led *led = hid_get_drvdata(hdev);
> +
> + if (led->mode == GT683R_LED_NORMAL)
> + sysfs_mode = 0;
> + else if (led->mode == GT683R_LED_AUDIO)
> + sysfs_mode = 1;
> + else
> + sysfs_mode = 2;
> +
> + return scnprintf(buf, PAGE_SIZE, "%u\n", sysfs_mode);
> +}
> +
> +static ssize_t leds_mode_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + u8 sysfs_mode;
> + struct hid_device *hdev = container_of(dev, struct hid_device, dev);
> + struct gt683r_led *led = hid_get_drvdata(hdev);
> +
> +
> + if (kstrtou8(buf, 10, &sysfs_mode) || sysfs_mode > 2)
> + return -EINVAL;
> +
> + mutex_lock(&led->lock);
> +
> + if (sysfs_mode == 0)
> + led->mode = GT683R_LED_NORMAL;
> + else if (sysfs_mode == 1)
> + led->mode = GT683R_LED_AUDIO;
> + else
> + led->mode = GT683R_LED_BREATHING;
> +
> + mutex_unlock(&led->lock);
> + schedule_work(&led->work);
> +
> + return count;
> +}
> +
> +static int gt683r_led_snd_msg(struct gt683r_led *led, u8 *msg)
> +{
> + int ret;
> +
> + ret = hid_hw_raw_request(led->hdev, msg[0], msg, GT683R_BUFFER_SIZE,
> + HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
> + if (ret != GT683R_BUFFER_SIZE) {
> + hid_err(led->hdev,
> + "failed to send set report request: %i\n", ret);
> + if (ret < 0)
> + return ret;
> + return -EIO;
> + }
> +
> + return 0;
> +}
> +
> +static int gt683r_leds_set(struct gt683r_led *led, u8 leds)
> +{
> + int ret;
> + u8 *buffer;
> +
> + buffer = kzalloc(GT683R_BUFFER_SIZE, GFP_KERNEL);
> + if (!buffer)
> + return -ENOMEM;
> +
> + buffer[0] = 0x01;
> + buffer[1] = 0x02;
> + buffer[2] = 0x30;
> + buffer[3] = leds;
> + ret = gt683r_led_snd_msg(led, buffer);
> +
> + kfree(buffer);
> + return ret;
> +}
> +
> +static int gt683r_mode_set(struct gt683r_led *led, u8 mode)
> +{
> + int ret;
> + u8 *buffer;
> +
> + buffer = kzalloc(GT683R_BUFFER_SIZE, GFP_KERNEL);
> + if (!buffer)
> + return -ENOMEM;
> +
> + buffer[0] = 0x01;
> + buffer[1] = 0x02;
> + buffer[2] = 0x20;
> + buffer[3] = mode;
> + buffer[4] = 0x01;
> + ret = gt683r_led_snd_msg(led, buffer);
> +
> + kfree(buffer);
> + return ret;
> +}
> +
> +static void gt683r_led_work(struct work_struct *work)
> +{
> + int i;
> + u8 leds = 0;
> + u8 mode;
> + struct gt683r_led *led = container_of(work, struct gt683r_led, work);
> +
> + mutex_lock(&led->lock);
> +
> + for (i = 0; i < GT683R_LED_COUNT; i++) {
> + if (led->brightnesses[i])
> + leds |= BIT(i);
> + }
> +
> + if (gt683r_leds_set(led, leds))
> + goto fail;
> +
> + if (leds)
> + mode = led->mode;
> + else
> + mode = GT683R_LED_OFF;
> +
> + gt683r_mode_set(led, mode);
> +fail:
> + mutex_unlock(&led->lock);
> +}
> +
> +static DEVICE_ATTR_RW(leds_mode);
> +
> +static int gt683r_led_probe(struct hid_device *hdev,
> + const struct hid_device_id *id)
> +{
> + int i, ret, name_sz;

One declaration per line.

> + char *name;
> + struct gt683r_led *led;
> +
> + led = devm_kzalloc(&hdev->dev, sizeof(struct gt683r_led), GFP_KERNEL);

Preferred form for passing the size to kmalloc and friends is
sizeof(*led) (in case the type of led ever changes). It's also shorter
and often more readable.

> + if (!led)
> + return -ENOMEM;
> +
> + led->mode = GT683R_LED_NORMAL;
> + led->hdev = hdev;
> + hid_set_drvdata(hdev, led);
> +
> + ret = hid_parse(hdev);
> + if (ret) {
> + hid_err(hdev, "hid parsing failed\n");
> + return ret;
> + }
> +
> + ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
> + if (ret) {
> + hid_err(hdev, "hw start failed\n");
> + return ret;
> + }
> +
> + for (i = 0; i < GT683R_LED_COUNT; i++) {
> + name_sz = strlen(dev_name(&hdev->dev)) +
> + strlen(gt683r_panel_names[i]) + 3;

Try to indent continuation lines at least two tabs further than the
preceding line.

> +
> + name = devm_kzalloc(&hdev->dev, sizeof(char) * name_sz,
> + GFP_KERNEL);

No need to do sizeof(char) which is always 1 (so no need to break this
line either).

> + if (!name) {
> + ret = -ENOMEM;
> + goto fail;
> + }
> +
> + snprintf(name, name_sz, "%s::%s",
> + dev_name(&hdev->dev), gt683r_panel_names[i]);

On more tab here.

> + led->led_devs[i].name = name;
> + led->led_devs[i].max_brightness = 1;
> + led->led_devs[i].brightness_set = gt683r_brightness_set;
> + ret = led_classdev_register(&hdev->dev, &led->led_devs[i]);
> + if (ret) {
> + hid_err(hdev, "could not register led device\n");
> + goto fail;
> + }
> + }
> +
> + ret = device_create_file(&led->hdev->dev, &dev_attr_leds_mode);
> + if (ret) {
> + hid_err(hdev, "could not make mode attribute file\n");
> + goto fail;
> + }
> +
> + mutex_init(&led->lock);
> + INIT_WORK(&led->work, gt683r_led_work);
> +
> + return 0;
> +
> +fail:
> + for (i = i - 1; i >= 0; i--)
> + led_classdev_unregister(&led->led_devs[i]);
> + hid_hw_stop(hdev);
> + return ret;
> +}
> +
> +static void gt683r_led_remove(struct hid_device *hdev)
> +{
> + int i;
> + struct gt683r_led *led = hid_get_drvdata(hdev);
> +
> + device_remove_file(&hdev->dev, &dev_attr_leds_mode);
> + for (i = 0; i < GT683R_LED_COUNT; i++)
> + led_classdev_unregister(&led->led_devs[i]);
> + flush_work(&led->work);
> + hid_hw_stop(hdev);
> +}
> +
> +static struct hid_driver gt683r_led_driver = {
> + .probe = gt683r_led_probe,
> + .remove = gt683r_led_remove,
> + .name = "gt683r_led",
> + .id_table = gt683r_led_id,
> +};
> +
> +module_hid_driver(gt683r_led_driver);
> +
> +MODULE_AUTHOR("Janne Kanniainen");
> +MODULE_DESCRIPTION("MSI GT683R led driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
> index 34bb220..3692d37 100644
> --- a/drivers/hid/hid-ids.h
> +++ b/drivers/hid/hid-ids.h
> @@ -641,7 +641,7 @@
> #define USB_DEVICE_ID_GENIUS_KB29E 0x3004
>
> #define USB_VENDOR_ID_MSI 0x1770
> -#define USB_DEVICE_ID_MSI_GX680R_LED_PANEL 0xff00
> +#define USB_DEVICE_ID_MSI_GT683R_LED_PANEL 0xff00
>
> #define USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR 0x0400
> #define USB_DEVICE_ID_N_S_HARMONY 0xc359
> diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
> index 8e4ddb3..c640e1d 100644
> --- a/drivers/hid/usbhid/hid-quirks.c
> +++ b/drivers/hid/usbhid/hid-quirks.c
> @@ -73,7 +73,7 @@ static const struct hid_blacklist {
> { USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS },
> { USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET },
> { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET },
> - { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GX680R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
> + { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
> { USB_VENDOR_ID_NEXIO, USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750, HID_QUIRK_NO_INIT_REPORTS },
> { USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_MOUSE, HID_QUIRK_NO_INIT_REPORTS },
> { USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NO_INIT_REPORTS },

Thanks,
Johan

2014-06-18 16:05:18

by Janne Kanniainen

[permalink] [raw]
Subject: [PATCH v9] leds: USB: HID: Add support for MSI GT683R led panels

This driver adds support for USB controlled led panels that exists in
MSI GT683R laptop

Signed-off-by: Janne Kanniainen <[email protected]>
---
Changes in v2:
- sorted headers to alphabetic order
- using devm_kzalloc
- using BIT(n)
- using usb_control_msg instead of usb_submit_urb
- removing unneeded code
Changes in v3:
- implemented as HID device
- some cleanups and bug fixes

Changes in v4:
- more cleanups
- support for selecting leds
- suppport for selecting status

Changes in v5:
- mode attribute documented under Documentation/ABI
- made array for led_classdev
- led devices uses now recommended naming scheme

Changes in v6:
- flush_work added
- using hid device name instead of hard coded gt683r
- allocating name buffers with devm_kzalloc

Changes in v7:
- buf with for fixed

Changes in v8:
- some cleanups and bugs fixed

Changes in v9:
- few style issues fixed

.../ABI/testing/sysfs-class-hid-driver-gt683r | 14 +
drivers/hid/Kconfig | 14 +
drivers/hid/Makefile | 1 +
drivers/hid/hid-core.c | 1 +
drivers/hid/hid-gt683r.c | 309 +++++++++++++++++++++
drivers/hid/hid-ids.h | 2 +-
drivers/hid/usbhid/hid-quirks.c | 2 +-
7 files changed, 341 insertions(+), 2 deletions(-)
create mode 100644 Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
create mode 100644 drivers/hid/hid-gt683r.c

diff --git a/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
new file mode 100644
index 0000000..317e9d5
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
@@ -0,0 +1,14 @@
+What: /sys/class/hidraw/<hidraw>/device/leds_mode
+Date: Jun 2014
+KernelVersion: 3.17
+Contact: Janne Kanniainen <[email protected]>
+Description:
+ Set the mode of LEDs
+
+ 0 - normal
+ 1 - audio
+ 2 - breathing
+
+ Normal: LEDs are fully on when enabled
+ Audio: LEDs brightness depends on sound level
+ Breathing: LEDs brightness varies at human breathing rate
\ No newline at end of file
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 7af9d0b..e2f4590 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -261,6 +261,20 @@ config HOLTEK_FF
Say Y here if you have a Holtek On Line Grip based game controller
and want to have force feedback support for it.

+config HID_GT683R
+ tristate "MSI GT68xR LED support"
+ depends on LEDS_CLASS && USB_HID
+ ---help---
+ Say Y here if you want to enable support for the three MSI GT68xR LEDs
+
+ This driver support following modes:
+ - Normal: LEDs are fully on when enabled
+ - Audio: LEDs brightness depends on sound level
+ - Breathing: LEDs brightness varies at human breathing rate
+
+ Currently the following devices are know to be supported:
+ - MSI GT683R
+
config HID_HUION
tristate "Huion tablets"
depends on USB_HID
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index fc712dd..7129311 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -48,6 +48,7 @@ obj-$(CONFIG_HID_EMS_FF) += hid-emsff.o
obj-$(CONFIG_HID_ELECOM) += hid-elecom.o
obj-$(CONFIG_HID_ELO) += hid-elo.o
obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o
+obj-$(CONFIG_HID_GT683R) += hid-gt683r.o
obj-$(CONFIG_HID_GYRATION) += hid-gyration.o
obj-$(CONFIG_HID_HOLTEK) += hid-holtek-kbd.o
obj-$(CONFIG_HID_HOLTEK) += hid-holtek-mouse.o
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index da52279..ec88fdb 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1827,6 +1827,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_OFFICE_KB) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) },
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_1) },
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_2) },
diff --git a/drivers/hid/hid-gt683r.c b/drivers/hid/hid-gt683r.c
new file mode 100644
index 0000000..077f7a1
--- /dev/null
+++ b/drivers/hid/hid-gt683r.c
@@ -0,0 +1,309 @@
+/*
+ * MSI GT683R led driver
+ *
+ * Copyright (c) 2014 Janne Kanniainen <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+#define GT683R_BUFFER_SIZE 8
+
+/*
+ * GT683R_LED_OFF: all LEDs are off
+ * GT683R_LED_AUDIO: LEDs brightness depends on sound level
+ * GT683R_LED_BREATHING: LEDs brightness varies at human breathing rate
+ * GT683R_LED_NORMAL: LEDs are fully on when enabled
+ */
+enum gt683r_led_mode {
+ GT683R_LED_OFF = 0,
+ GT683R_LED_AUDIO = 2,
+ GT683R_LED_BREATHING = 3,
+ GT683R_LED_NORMAL = 5
+};
+
+enum gt683r_panels {
+ GT683R_LED_BACK = 0,
+ GT683R_LED_SIDE = 1,
+ GT683R_LED_FRONT = 2,
+ GT683R_LED_COUNT,
+};
+
+static const char * const gt683r_panel_names[] = {
+ "back",
+ "side",
+ "front",
+};
+
+struct gt683r_led {
+ struct hid_device *hdev;
+ struct led_classdev led_devs[GT683R_LED_COUNT];
+ struct mutex lock;
+ struct work_struct work;
+ enum led_brightness brightnesses[GT683R_LED_COUNT];
+ enum gt683r_led_mode mode;
+};
+
+static const struct hid_device_id gt683r_led_id[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
+ { }
+};
+
+static void gt683r_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ int i;
+ struct device *dev = led_cdev->dev->parent;
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct gt683r_led *led = hid_get_drvdata(hdev);
+
+ for (i = 0; i < GT683R_LED_COUNT; i++) {
+ if (led_cdev == &led->led_devs[i])
+ break;
+ }
+
+ if (i < GT683R_LED_COUNT) {
+ led->brightnesses[i] = brightness;
+ schedule_work(&led->work);
+ }
+}
+
+static ssize_t leds_mode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ u8 sysfs_mode;
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct gt683r_led *led = hid_get_drvdata(hdev);
+
+ if (led->mode == GT683R_LED_NORMAL)
+ sysfs_mode = 0;
+ else if (led->mode == GT683R_LED_AUDIO)
+ sysfs_mode = 1;
+ else
+ sysfs_mode = 2;
+
+ return scnprintf(buf, PAGE_SIZE, "%u\n", sysfs_mode);
+}
+
+static ssize_t leds_mode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ u8 sysfs_mode;
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct gt683r_led *led = hid_get_drvdata(hdev);
+
+
+ if (kstrtou8(buf, 10, &sysfs_mode) || sysfs_mode > 2)
+ return -EINVAL;
+
+ mutex_lock(&led->lock);
+
+ if (sysfs_mode == 0)
+ led->mode = GT683R_LED_NORMAL;
+ else if (sysfs_mode == 1)
+ led->mode = GT683R_LED_AUDIO;
+ else
+ led->mode = GT683R_LED_BREATHING;
+
+ mutex_unlock(&led->lock);
+ schedule_work(&led->work);
+
+ return count;
+}
+
+static int gt683r_led_snd_msg(struct gt683r_led *led, u8 *msg)
+{
+ int ret;
+
+ ret = hid_hw_raw_request(led->hdev, msg[0], msg, GT683R_BUFFER_SIZE,
+ HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
+ if (ret != GT683R_BUFFER_SIZE) {
+ hid_err(led->hdev,
+ "failed to send set report request: %i\n", ret);
+ if (ret < 0)
+ return ret;
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int gt683r_leds_set(struct gt683r_led *led, u8 leds)
+{
+ int ret;
+ u8 *buffer;
+
+ buffer = kzalloc(GT683R_BUFFER_SIZE, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
+ buffer[0] = 0x01;
+ buffer[1] = 0x02;
+ buffer[2] = 0x30;
+ buffer[3] = leds;
+ ret = gt683r_led_snd_msg(led, buffer);
+
+ kfree(buffer);
+ return ret;
+}
+
+static int gt683r_mode_set(struct gt683r_led *led, u8 mode)
+{
+ int ret;
+ u8 *buffer;
+
+ buffer = kzalloc(GT683R_BUFFER_SIZE, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
+ buffer[0] = 0x01;
+ buffer[1] = 0x02;
+ buffer[2] = 0x20;
+ buffer[3] = mode;
+ buffer[4] = 0x01;
+ ret = gt683r_led_snd_msg(led, buffer);
+
+ kfree(buffer);
+ return ret;
+}
+
+static void gt683r_led_work(struct work_struct *work)
+{
+ int i;
+ u8 leds = 0;
+ u8 mode;
+ struct gt683r_led *led = container_of(work, struct gt683r_led, work);
+
+ mutex_lock(&led->lock);
+
+ for (i = 0; i < GT683R_LED_COUNT; i++) {
+ if (led->brightnesses[i])
+ leds |= BIT(i);
+ }
+
+ if (gt683r_leds_set(led, leds))
+ goto fail;
+
+ if (leds)
+ mode = led->mode;
+ else
+ mode = GT683R_LED_OFF;
+
+ gt683r_mode_set(led, mode);
+fail:
+ mutex_unlock(&led->lock);
+}
+
+static DEVICE_ATTR_RW(leds_mode);
+
+static int gt683r_led_probe(struct hid_device *hdev,
+ const struct hid_device_id *id)
+{
+ int i;
+ int ret;
+ int name_sz;
+ char *name;
+ struct gt683r_led *led;
+
+ led = devm_kzalloc(&hdev->dev, sizeof(*led), GFP_KERNEL);
+ if (!led)
+ return -ENOMEM;
+
+ led->mode = GT683R_LED_NORMAL;
+ led->hdev = hdev;
+ hid_set_drvdata(hdev, led);
+
+ ret = hid_parse(hdev);
+ if (ret) {
+ hid_err(hdev, "hid parsing failed\n");
+ return ret;
+ }
+
+ ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
+ if (ret) {
+ hid_err(hdev, "hw start failed\n");
+ return ret;
+ }
+
+ for (i = 0; i < GT683R_LED_COUNT; i++) {
+ name_sz = strlen(dev_name(&hdev->dev)) +
+ strlen(gt683r_panel_names[i]) + 3;
+
+ name = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL);
+ if (!name) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ snprintf(name, name_sz, "%s::%s",
+ dev_name(&hdev->dev), gt683r_panel_names[i]);
+ led->led_devs[i].name = name;
+ led->led_devs[i].max_brightness = 1;
+ led->led_devs[i].brightness_set = gt683r_brightness_set;
+ ret = led_classdev_register(&hdev->dev, &led->led_devs[i]);
+ if (ret) {
+ hid_err(hdev, "could not register led device\n");
+ goto fail;
+ }
+ }
+
+ ret = device_create_file(&led->hdev->dev, &dev_attr_leds_mode);
+ if (ret) {
+ hid_err(hdev, "could not make mode attribute file\n");
+ goto fail;
+ }
+
+ mutex_init(&led->lock);
+ INIT_WORK(&led->work, gt683r_led_work);
+
+ return 0;
+
+fail:
+ for (i = i - 1; i >= 0; i--)
+ led_classdev_unregister(&led->led_devs[i]);
+ hid_hw_stop(hdev);
+ return ret;
+}
+
+static void gt683r_led_remove(struct hid_device *hdev)
+{
+ int i;
+ struct gt683r_led *led = hid_get_drvdata(hdev);
+
+ device_remove_file(&hdev->dev, &dev_attr_leds_mode);
+ for (i = 0; i < GT683R_LED_COUNT; i++)
+ led_classdev_unregister(&led->led_devs[i]);
+ flush_work(&led->work);
+ hid_hw_stop(hdev);
+}
+
+static struct hid_driver gt683r_led_driver = {
+ .probe = gt683r_led_probe,
+ .remove = gt683r_led_remove,
+ .name = "gt683r_led",
+ .id_table = gt683r_led_id,
+};
+
+module_hid_driver(gt683r_led_driver);
+
+MODULE_AUTHOR("Janne Kanniainen");
+MODULE_DESCRIPTION("MSI GT683R led driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 34bb220..3692d37 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -641,7 +641,7 @@
#define USB_DEVICE_ID_GENIUS_KB29E 0x3004

#define USB_VENDOR_ID_MSI 0x1770
-#define USB_DEVICE_ID_MSI_GX680R_LED_PANEL 0xff00
+#define USB_DEVICE_ID_MSI_GT683R_LED_PANEL 0xff00

#define USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR 0x0400
#define USB_DEVICE_ID_N_S_HARMONY 0xc359
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
index 8e4ddb3..c640e1d 100644
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -73,7 +73,7 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET },
- { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GX680R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
+ { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_NEXIO, USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_MOUSE, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NO_INIT_REPORTS },
--
1.9.2

2014-06-18 16:12:30

by Johan Hovold

[permalink] [raw]
Subject: Re: [PATCH v9] leds: USB: HID: Add support for MSI GT683R led panels

On Wed, Jun 18, 2014 at 07:05:02PM +0300, Janne Kanniainen wrote:
> This driver adds support for USB controlled led panels that exists in
> MSI GT683R laptop
>
> Signed-off-by: Janne Kanniainen <[email protected]>

Reviewed-by: Johan Hovold <[email protected]>

Thanks, Janne!

Johan

> ---
> Changes in v2:
> - sorted headers to alphabetic order
> - using devm_kzalloc
> - using BIT(n)
> - using usb_control_msg instead of usb_submit_urb
> - removing unneeded code
> Changes in v3:
> - implemented as HID device
> - some cleanups and bug fixes
>
> Changes in v4:
> - more cleanups
> - support for selecting leds
> - suppport for selecting status
>
> Changes in v5:
> - mode attribute documented under Documentation/ABI
> - made array for led_classdev
> - led devices uses now recommended naming scheme
>
> Changes in v6:
> - flush_work added
> - using hid device name instead of hard coded gt683r
> - allocating name buffers with devm_kzalloc
>
> Changes in v7:
> - buf with for fixed
>
> Changes in v8:
> - some cleanups and bugs fixed
>
> Changes in v9:
> - few style issues fixed
>
> .../ABI/testing/sysfs-class-hid-driver-gt683r | 14 +
> drivers/hid/Kconfig | 14 +
> drivers/hid/Makefile | 1 +
> drivers/hid/hid-core.c | 1 +
> drivers/hid/hid-gt683r.c | 309 +++++++++++++++++++++
> drivers/hid/hid-ids.h | 2 +-
> drivers/hid/usbhid/hid-quirks.c | 2 +-
> 7 files changed, 341 insertions(+), 2 deletions(-)
> create mode 100644 Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
> create mode 100644 drivers/hid/hid-gt683r.c
>
> diff --git a/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
> new file mode 100644
> index 0000000..317e9d5
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
> @@ -0,0 +1,14 @@
> +What: /sys/class/hidraw/<hidraw>/device/leds_mode
> +Date: Jun 2014
> +KernelVersion: 3.17
> +Contact: Janne Kanniainen <[email protected]>
> +Description:
> + Set the mode of LEDs
> +
> + 0 - normal
> + 1 - audio
> + 2 - breathing
> +
> + Normal: LEDs are fully on when enabled
> + Audio: LEDs brightness depends on sound level
> + Breathing: LEDs brightness varies at human breathing rate
> \ No newline at end of file
> diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
> index 7af9d0b..e2f4590 100644
> --- a/drivers/hid/Kconfig
> +++ b/drivers/hid/Kconfig
> @@ -261,6 +261,20 @@ config HOLTEK_FF
> Say Y here if you have a Holtek On Line Grip based game controller
> and want to have force feedback support for it.
>
> +config HID_GT683R
> + tristate "MSI GT68xR LED support"
> + depends on LEDS_CLASS && USB_HID
> + ---help---
> + Say Y here if you want to enable support for the three MSI GT68xR LEDs
> +
> + This driver support following modes:
> + - Normal: LEDs are fully on when enabled
> + - Audio: LEDs brightness depends on sound level
> + - Breathing: LEDs brightness varies at human breathing rate
> +
> + Currently the following devices are know to be supported:
> + - MSI GT683R
> +
> config HID_HUION
> tristate "Huion tablets"
> depends on USB_HID
> diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
> index fc712dd..7129311 100644
> --- a/drivers/hid/Makefile
> +++ b/drivers/hid/Makefile
> @@ -48,6 +48,7 @@ obj-$(CONFIG_HID_EMS_FF) += hid-emsff.o
> obj-$(CONFIG_HID_ELECOM) += hid-elecom.o
> obj-$(CONFIG_HID_ELO) += hid-elo.o
> obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o
> +obj-$(CONFIG_HID_GT683R) += hid-gt683r.o
> obj-$(CONFIG_HID_GYRATION) += hid-gyration.o
> obj-$(CONFIG_HID_HOLTEK) += hid-holtek-kbd.o
> obj-$(CONFIG_HID_HOLTEK) += hid-holtek-mouse.o
> diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
> index da52279..ec88fdb 100644
> --- a/drivers/hid/hid-core.c
> +++ b/drivers/hid/hid-core.c
> @@ -1827,6 +1827,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
> { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) },
> { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_OFFICE_KB) },
> { HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) },
> + { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
> { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) },
> { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_1) },
> { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_2) },
> diff --git a/drivers/hid/hid-gt683r.c b/drivers/hid/hid-gt683r.c
> new file mode 100644
> index 0000000..077f7a1
> --- /dev/null
> +++ b/drivers/hid/hid-gt683r.c
> @@ -0,0 +1,309 @@
> +/*
> + * MSI GT683R led driver
> + *
> + * Copyright (c) 2014 Janne Kanniainen <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/device.h>
> +#include <linux/hid.h>
> +#include <linux/kernel.h>
> +#include <linux/leds.h>
> +#include <linux/module.h>
> +
> +#include "hid-ids.h"
> +
> +#define GT683R_BUFFER_SIZE 8
> +
> +/*
> + * GT683R_LED_OFF: all LEDs are off
> + * GT683R_LED_AUDIO: LEDs brightness depends on sound level
> + * GT683R_LED_BREATHING: LEDs brightness varies at human breathing rate
> + * GT683R_LED_NORMAL: LEDs are fully on when enabled
> + */
> +enum gt683r_led_mode {
> + GT683R_LED_OFF = 0,
> + GT683R_LED_AUDIO = 2,
> + GT683R_LED_BREATHING = 3,
> + GT683R_LED_NORMAL = 5
> +};
> +
> +enum gt683r_panels {
> + GT683R_LED_BACK = 0,
> + GT683R_LED_SIDE = 1,
> + GT683R_LED_FRONT = 2,
> + GT683R_LED_COUNT,
> +};
> +
> +static const char * const gt683r_panel_names[] = {
> + "back",
> + "side",
> + "front",
> +};
> +
> +struct gt683r_led {
> + struct hid_device *hdev;
> + struct led_classdev led_devs[GT683R_LED_COUNT];
> + struct mutex lock;
> + struct work_struct work;
> + enum led_brightness brightnesses[GT683R_LED_COUNT];
> + enum gt683r_led_mode mode;
> +};
> +
> +static const struct hid_device_id gt683r_led_id[] = {
> + { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
> + { }
> +};
> +
> +static void gt683r_brightness_set(struct led_classdev *led_cdev,
> + enum led_brightness brightness)
> +{
> + int i;
> + struct device *dev = led_cdev->dev->parent;
> + struct hid_device *hdev = container_of(dev, struct hid_device, dev);
> + struct gt683r_led *led = hid_get_drvdata(hdev);
> +
> + for (i = 0; i < GT683R_LED_COUNT; i++) {
> + if (led_cdev == &led->led_devs[i])
> + break;
> + }
> +
> + if (i < GT683R_LED_COUNT) {
> + led->brightnesses[i] = brightness;
> + schedule_work(&led->work);
> + }
> +}
> +
> +static ssize_t leds_mode_show(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> +{
> + u8 sysfs_mode;
> + struct hid_device *hdev = container_of(dev, struct hid_device, dev);
> + struct gt683r_led *led = hid_get_drvdata(hdev);
> +
> + if (led->mode == GT683R_LED_NORMAL)
> + sysfs_mode = 0;
> + else if (led->mode == GT683R_LED_AUDIO)
> + sysfs_mode = 1;
> + else
> + sysfs_mode = 2;
> +
> + return scnprintf(buf, PAGE_SIZE, "%u\n", sysfs_mode);
> +}
> +
> +static ssize_t leds_mode_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + u8 sysfs_mode;
> + struct hid_device *hdev = container_of(dev, struct hid_device, dev);
> + struct gt683r_led *led = hid_get_drvdata(hdev);
> +
> +
> + if (kstrtou8(buf, 10, &sysfs_mode) || sysfs_mode > 2)
> + return -EINVAL;
> +
> + mutex_lock(&led->lock);
> +
> + if (sysfs_mode == 0)
> + led->mode = GT683R_LED_NORMAL;
> + else if (sysfs_mode == 1)
> + led->mode = GT683R_LED_AUDIO;
> + else
> + led->mode = GT683R_LED_BREATHING;
> +
> + mutex_unlock(&led->lock);
> + schedule_work(&led->work);
> +
> + return count;
> +}
> +
> +static int gt683r_led_snd_msg(struct gt683r_led *led, u8 *msg)
> +{
> + int ret;
> +
> + ret = hid_hw_raw_request(led->hdev, msg[0], msg, GT683R_BUFFER_SIZE,
> + HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
> + if (ret != GT683R_BUFFER_SIZE) {
> + hid_err(led->hdev,
> + "failed to send set report request: %i\n", ret);
> + if (ret < 0)
> + return ret;
> + return -EIO;
> + }
> +
> + return 0;
> +}
> +
> +static int gt683r_leds_set(struct gt683r_led *led, u8 leds)
> +{
> + int ret;
> + u8 *buffer;
> +
> + buffer = kzalloc(GT683R_BUFFER_SIZE, GFP_KERNEL);
> + if (!buffer)
> + return -ENOMEM;
> +
> + buffer[0] = 0x01;
> + buffer[1] = 0x02;
> + buffer[2] = 0x30;
> + buffer[3] = leds;
> + ret = gt683r_led_snd_msg(led, buffer);
> +
> + kfree(buffer);
> + return ret;
> +}
> +
> +static int gt683r_mode_set(struct gt683r_led *led, u8 mode)
> +{
> + int ret;
> + u8 *buffer;
> +
> + buffer = kzalloc(GT683R_BUFFER_SIZE, GFP_KERNEL);
> + if (!buffer)
> + return -ENOMEM;
> +
> + buffer[0] = 0x01;
> + buffer[1] = 0x02;
> + buffer[2] = 0x20;
> + buffer[3] = mode;
> + buffer[4] = 0x01;
> + ret = gt683r_led_snd_msg(led, buffer);
> +
> + kfree(buffer);
> + return ret;
> +}
> +
> +static void gt683r_led_work(struct work_struct *work)
> +{
> + int i;
> + u8 leds = 0;
> + u8 mode;
> + struct gt683r_led *led = container_of(work, struct gt683r_led, work);
> +
> + mutex_lock(&led->lock);
> +
> + for (i = 0; i < GT683R_LED_COUNT; i++) {
> + if (led->brightnesses[i])
> + leds |= BIT(i);
> + }
> +
> + if (gt683r_leds_set(led, leds))
> + goto fail;
> +
> + if (leds)
> + mode = led->mode;
> + else
> + mode = GT683R_LED_OFF;
> +
> + gt683r_mode_set(led, mode);
> +fail:
> + mutex_unlock(&led->lock);
> +}
> +
> +static DEVICE_ATTR_RW(leds_mode);
> +
> +static int gt683r_led_probe(struct hid_device *hdev,
> + const struct hid_device_id *id)
> +{
> + int i;
> + int ret;
> + int name_sz;
> + char *name;
> + struct gt683r_led *led;
> +
> + led = devm_kzalloc(&hdev->dev, sizeof(*led), GFP_KERNEL);
> + if (!led)
> + return -ENOMEM;
> +
> + led->mode = GT683R_LED_NORMAL;
> + led->hdev = hdev;
> + hid_set_drvdata(hdev, led);
> +
> + ret = hid_parse(hdev);
> + if (ret) {
> + hid_err(hdev, "hid parsing failed\n");
> + return ret;
> + }
> +
> + ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
> + if (ret) {
> + hid_err(hdev, "hw start failed\n");
> + return ret;
> + }
> +
> + for (i = 0; i < GT683R_LED_COUNT; i++) {
> + name_sz = strlen(dev_name(&hdev->dev)) +
> + strlen(gt683r_panel_names[i]) + 3;
> +
> + name = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL);
> + if (!name) {
> + ret = -ENOMEM;
> + goto fail;
> + }
> +
> + snprintf(name, name_sz, "%s::%s",
> + dev_name(&hdev->dev), gt683r_panel_names[i]);
> + led->led_devs[i].name = name;
> + led->led_devs[i].max_brightness = 1;
> + led->led_devs[i].brightness_set = gt683r_brightness_set;
> + ret = led_classdev_register(&hdev->dev, &led->led_devs[i]);
> + if (ret) {
> + hid_err(hdev, "could not register led device\n");
> + goto fail;
> + }
> + }
> +
> + ret = device_create_file(&led->hdev->dev, &dev_attr_leds_mode);
> + if (ret) {
> + hid_err(hdev, "could not make mode attribute file\n");
> + goto fail;
> + }
> +
> + mutex_init(&led->lock);
> + INIT_WORK(&led->work, gt683r_led_work);
> +
> + return 0;
> +
> +fail:
> + for (i = i - 1; i >= 0; i--)
> + led_classdev_unregister(&led->led_devs[i]);
> + hid_hw_stop(hdev);
> + return ret;
> +}
> +
> +static void gt683r_led_remove(struct hid_device *hdev)
> +{
> + int i;
> + struct gt683r_led *led = hid_get_drvdata(hdev);
> +
> + device_remove_file(&hdev->dev, &dev_attr_leds_mode);
> + for (i = 0; i < GT683R_LED_COUNT; i++)
> + led_classdev_unregister(&led->led_devs[i]);
> + flush_work(&led->work);
> + hid_hw_stop(hdev);
> +}
> +
> +static struct hid_driver gt683r_led_driver = {
> + .probe = gt683r_led_probe,
> + .remove = gt683r_led_remove,
> + .name = "gt683r_led",
> + .id_table = gt683r_led_id,
> +};
> +
> +module_hid_driver(gt683r_led_driver);
> +
> +MODULE_AUTHOR("Janne Kanniainen");
> +MODULE_DESCRIPTION("MSI GT683R led driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
> index 34bb220..3692d37 100644
> --- a/drivers/hid/hid-ids.h
> +++ b/drivers/hid/hid-ids.h
> @@ -641,7 +641,7 @@
> #define USB_DEVICE_ID_GENIUS_KB29E 0x3004
>
> #define USB_VENDOR_ID_MSI 0x1770
> -#define USB_DEVICE_ID_MSI_GX680R_LED_PANEL 0xff00
> +#define USB_DEVICE_ID_MSI_GT683R_LED_PANEL 0xff00
>
> #define USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR 0x0400
> #define USB_DEVICE_ID_N_S_HARMONY 0xc359
> diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
> index 8e4ddb3..c640e1d 100644
> --- a/drivers/hid/usbhid/hid-quirks.c
> +++ b/drivers/hid/usbhid/hid-quirks.c
> @@ -73,7 +73,7 @@ static const struct hid_blacklist {
> { USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS },
> { USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET },
> { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET },
> - { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GX680R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
> + { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
> { USB_VENDOR_ID_NEXIO, USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750, HID_QUIRK_NO_INIT_REPORTS },
> { USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_MOUSE, HID_QUIRK_NO_INIT_REPORTS },
> { USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NO_INIT_REPORTS },

2014-06-18 18:41:38

by Janne Kanniainen

[permalink] [raw]
Subject: Re: [PATCH v9] leds: USB: HID: Add support for MSI GT683R led panels

>> This driver adds support for USB controlled led panels that exists in
>> MSI GT683R laptop
>>
>> Signed-off-by: Janne Kanniainen <[email protected]>
>
> Reviewed-by: Johan Hovold <[email protected]>
>
> Thanks, Janne!
>
> Johan

Thank you for reviewing my patch :) I sure learnt a
lot from you and if I ever send patch in future, it is for sure much
easier (Maybe I get it trough in 8th time ;))

Janne

2014-06-18 18:47:05

by Johan Hovold

[permalink] [raw]
Subject: Re: [PATCH v9] leds: USB: HID: Add support for MSI GT683R led panels

On Wed, Jun 18, 2014 at 09:41:35PM +0300, Janne Kanniainen wrote:
> >> This driver adds support for USB controlled led panels that exists in
> >> MSI GT683R laptop
> >>
> >> Signed-off-by: Janne Kanniainen <[email protected]>
> >
> > Reviewed-by: Johan Hovold <[email protected]>
> >
> > Thanks, Janne!
> >
> > Johan
>
> Thank you for reviewing my patch :) I sure learnt a
> lot from you and if I ever send patch in future, it is for sure much
> easier (Maybe I get it trough in 8th time ;))

My pleasure. It's indeed a very good first patch you have created.

Good luck with the next one! ;)

Johan

2014-06-18 22:10:36

by Jiri Kosina

[permalink] [raw]
Subject: Re: [PATCH v9] leds: USB: HID: Add support for MSI GT683R led panels

On Wed, 18 Jun 2014, Johan Hovold wrote:

> > This driver adds support for USB controlled led panels that exists in
> > MSI GT683R laptop
> >
> > Signed-off-by: Janne Kanniainen <[email protected]>
>
> Reviewed-by: Johan Hovold <[email protected]>
>
> Thanks, Janne!

Now applied. Thanks Janne for the driver, and thanks a lot Johan for a
very careful review, I really appreciate it.

--
Jiri Kosina
SUSE Labs

2014-06-23 14:35:18

by Oliver Neukum

[permalink] [raw]
Subject: Re: [PATCH v9] leds: USB: HID: Add support for MSI GT683R led panels

On Wed, 2014-06-18 at 19:05 +0300, Janne Kanniainen wrote:
> This driver adds support for USB controlled led panels that exists in
> MSI GT683R laptop
>

> +static int gt683r_led_probe(struct hid_device *hdev,
> + const struct hid_device_id *id)
> +{
> + int i;
> + int ret;
> + int name_sz;
> + char *name;
> + struct gt683r_led *led;
> +
> + led = devm_kzalloc(&hdev->dev, sizeof(*led), GFP_KERNEL);
> + if (!led)
> + return -ENOMEM;
> +
> + led->mode = GT683R_LED_NORMAL;
> + led->hdev = hdev;
> + hid_set_drvdata(hdev, led);
> +
> + ret = hid_parse(hdev);
> + if (ret) {
> + hid_err(hdev, "hid parsing failed\n");
> + return ret;
> + }
> +
> + ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
> + if (ret) {
> + hid_err(hdev, "hw start failed\n");
> + return ret;
> + }
> +
> + for (i = 0; i < GT683R_LED_COUNT; i++) {
> + name_sz = strlen(dev_name(&hdev->dev)) +
> + strlen(gt683r_panel_names[i]) + 3;
> +
> + name = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL);
> + if (!name) {
> + ret = -ENOMEM;
> + goto fail;
> + }
> +
> + snprintf(name, name_sz, "%s::%s",
> + dev_name(&hdev->dev), gt683r_panel_names[i]);
> + led->led_devs[i].name = name;
> + led->led_devs[i].max_brightness = 1;
> + led->led_devs[i].brightness_set = gt683r_brightness_set;
> + ret = led_classdev_register(&hdev->dev, &led->led_devs[i]);
> + if (ret) {
> + hid_err(hdev, "could not register led device\n");
> + goto fail;
> + }
> + }
> +
> + ret = device_create_file(&led->hdev->dev, &dev_attr_leds_mode);
> + if (ret) {
> + hid_err(hdev, "could not make mode attribute file\n");
> + goto fail;
> + }
> +

This is the window.

> + mutex_init(&led->lock);
> + INIT_WORK(&led->work, gt683r_led_work);
> +

And here we have a problem. This is a race condition.
At this time you've already created the sysfs files. So
their methods can be called. They will lock a mutex and/or
schedule work that hasn't been initialized.
The initialization must be done before anything in sysfs
is created.

> + return 0;
> +
> +fail:
> + for (i = i - 1; i >= 0; i--)
> + led_classdev_unregister(&led->led_devs[i]);
> + hid_hw_stop(hdev);
> + return ret;
> +}
> +

Sorry for noticing this thread late.

Regards
Oliver

2014-06-23 14:43:34

by Johan Hovold

[permalink] [raw]
Subject: Re: [PATCH v9] leds: USB: HID: Add support for MSI GT683R led panels

On Mon, Jun 23, 2014 at 04:35:13PM +0200, Oliver Neukum wrote:
> On Wed, 2014-06-18 at 19:05 +0300, Janne Kanniainen wrote:
> > This driver adds support for USB controlled led panels that exists in
> > MSI GT683R laptop
> >
>
> > +static int gt683r_led_probe(struct hid_device *hdev,
> > + const struct hid_device_id *id)
> > +{
> > + int i;
> > + int ret;
> > + int name_sz;
> > + char *name;
> > + struct gt683r_led *led;
> > +
> > + led = devm_kzalloc(&hdev->dev, sizeof(*led), GFP_KERNEL);
> > + if (!led)
> > + return -ENOMEM;
> > +
> > + led->mode = GT683R_LED_NORMAL;
> > + led->hdev = hdev;
> > + hid_set_drvdata(hdev, led);
> > +
> > + ret = hid_parse(hdev);
> > + if (ret) {
> > + hid_err(hdev, "hid parsing failed\n");
> > + return ret;
> > + }
> > +
> > + ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
> > + if (ret) {
> > + hid_err(hdev, "hw start failed\n");
> > + return ret;
> > + }
> > +
> > + for (i = 0; i < GT683R_LED_COUNT; i++) {
> > + name_sz = strlen(dev_name(&hdev->dev)) +
> > + strlen(gt683r_panel_names[i]) + 3;
> > +
> > + name = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL);
> > + if (!name) {
> > + ret = -ENOMEM;
> > + goto fail;
> > + }
> > +
> > + snprintf(name, name_sz, "%s::%s",
> > + dev_name(&hdev->dev), gt683r_panel_names[i]);
> > + led->led_devs[i].name = name;
> > + led->led_devs[i].max_brightness = 1;
> > + led->led_devs[i].brightness_set = gt683r_brightness_set;
> > + ret = led_classdev_register(&hdev->dev, &led->led_devs[i]);
> > + if (ret) {
> > + hid_err(hdev, "could not register led device\n");
> > + goto fail;
> > + }
> > + }
> > +
> > + ret = device_create_file(&led->hdev->dev, &dev_attr_leds_mode);
> > + if (ret) {
> > + hid_err(hdev, "could not make mode attribute file\n");
> > + goto fail;
> > + }
> > +
>
> This is the window.
>
> > + mutex_init(&led->lock);
> > + INIT_WORK(&led->work, gt683r_led_work);
> > +
>
> And here we have a problem. This is a race condition.
> At this time you've already created the sysfs files. So
> their methods can be called. They will lock a mutex and/or
> schedule work that hasn't been initialized.
> The initialization must be done before anything in sysfs
> is created.

Just move the initialisation of the lock and work to the other private
data initialisations directly after it's allocated.

Can you send a follow up patch, Janne?

Thanks Oliver!

Johan

2014-06-23 16:17:35

by Greg KH

[permalink] [raw]
Subject: Re: [PATCH v9] leds: USB: HID: Add support for MSI GT683R led panels

On Mon, Jun 23, 2014 at 04:42:55PM +0200, Johan Hovold wrote:
> On Mon, Jun 23, 2014 at 04:35:13PM +0200, Oliver Neukum wrote:
> > On Wed, 2014-06-18 at 19:05 +0300, Janne Kanniainen wrote:
> > > This driver adds support for USB controlled led panels that exists in
> > > MSI GT683R laptop
> > >
> >
> > > +static int gt683r_led_probe(struct hid_device *hdev,
> > > + const struct hid_device_id *id)
> > > +{
> > > + int i;
> > > + int ret;
> > > + int name_sz;
> > > + char *name;
> > > + struct gt683r_led *led;
> > > +
> > > + led = devm_kzalloc(&hdev->dev, sizeof(*led), GFP_KERNEL);
> > > + if (!led)
> > > + return -ENOMEM;
> > > +
> > > + led->mode = GT683R_LED_NORMAL;
> > > + led->hdev = hdev;
> > > + hid_set_drvdata(hdev, led);
> > > +
> > > + ret = hid_parse(hdev);
> > > + if (ret) {
> > > + hid_err(hdev, "hid parsing failed\n");
> > > + return ret;
> > > + }
> > > +
> > > + ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
> > > + if (ret) {
> > > + hid_err(hdev, "hw start failed\n");
> > > + return ret;
> > > + }
> > > +
> > > + for (i = 0; i < GT683R_LED_COUNT; i++) {
> > > + name_sz = strlen(dev_name(&hdev->dev)) +
> > > + strlen(gt683r_panel_names[i]) + 3;
> > > +
> > > + name = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL);
> > > + if (!name) {
> > > + ret = -ENOMEM;
> > > + goto fail;
> > > + }
> > > +
> > > + snprintf(name, name_sz, "%s::%s",
> > > + dev_name(&hdev->dev), gt683r_panel_names[i]);
> > > + led->led_devs[i].name = name;
> > > + led->led_devs[i].max_brightness = 1;
> > > + led->led_devs[i].brightness_set = gt683r_brightness_set;
> > > + ret = led_classdev_register(&hdev->dev, &led->led_devs[i]);
> > > + if (ret) {
> > > + hid_err(hdev, "could not register led device\n");
> > > + goto fail;
> > > + }
> > > + }
> > > +
> > > + ret = device_create_file(&led->hdev->dev, &dev_attr_leds_mode);
> > > + if (ret) {
> > > + hid_err(hdev, "could not make mode attribute file\n");
> > > + goto fail;
> > > + }
> > > +
> >
> > This is the window.
> >
> > > + mutex_init(&led->lock);
> > > + INIT_WORK(&led->work, gt683r_led_work);
> > > +
> >
> > And here we have a problem. This is a race condition.
> > At this time you've already created the sysfs files. So
> > their methods can be called. They will lock a mutex and/or
> > schedule work that hasn't been initialized.
> > The initialization must be done before anything in sysfs
> > is created.
>
> Just move the initialisation of the lock and work to the other private
> data initialisations directly after it's allocated.
>
> Can you send a follow up patch, Janne?

No driver should be creating sysfs files directly, use the proper
attribute group interface instead. That way the files are created
_before_ the device is announced to userspace, instead of after, which
is a race condition there as well.

thanks,

greg k-h

2014-06-23 16:20:06

by Janne Kanniainen

[permalink] [raw]
Subject: Re: [PATCH v9] leds: USB: HID: Add support for MSI GT683R led panels

>> Sorry for noticing this thread late.

No problem. Good that you noticed it now! Thank you.

> Just move the initialisation of the lock and work to the other private
> data initialisations directly after it's allocated.
>
> Can you send a follow up patch, Janne?

Yes I can. Just a moment.

Janne

2014-06-23 17:17:08

by Janne Kanniainen

[permalink] [raw]
Subject: [PATCH v10] leds: USB: HID: Add support for MSI GT683R led panels

This driver adds support for USB controlled led panels that exists in
MSI GT683R laptop

Signed-off-by: Janne Kanniainen <[email protected]>
---
Changes in v2:
- sorted headers to alphabetic order
- using devm_kzalloc
- using BIT(n)
- using usb_control_msg instead of usb_submit_urb
- removing unneeded code
Changes in v3:
- implemented as HID device
- some cleanups and bug fixes
Changes in v4:
- more cleanups
- support for selecting leds
- suppport for selecting status

Changes in v5:
- mode attribute documented under Documentation/ABI
- made array for led_classdev
- led devices uses now recommended naming scheme

Changes in v6:
- flush_work added
- using hid device name instead of hard coded gt683r
- allocating name buffers with devm_kzalloc

Changes in v7:
- buf with for fixed

Changes in v8:
- some cleanups and bugs fixed

Changes in v9:
- few style issues fixed

Changes in v10:
- race condition fixed
- using proper attribute group

.../ABI/testing/sysfs-class-hid-driver-gt683r | 14 +
drivers/hid/Kconfig | 14 +
drivers/hid/Makefile | 1 +
drivers/hid/hid-core.c | 1 +
drivers/hid/hid-gt683r.c | 318 +++++++++++++++++++++
drivers/hid/hid-ids.h | 2 +-
drivers/hid/usbhid/hid-quirks.c | 2 +-
7 files changed, 350 insertions(+), 2 deletions(-)
create mode 100644 Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
create mode 100644 drivers/hid/hid-gt683r.c

diff --git a/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
new file mode 100644
index 0000000..317e9d5
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
@@ -0,0 +1,14 @@
+What: /sys/class/hidraw/<hidraw>/device/leds_mode
+Date: Jun 2014
+KernelVersion: 3.17
+Contact: Janne Kanniainen <[email protected]>
+Description:
+ Set the mode of LEDs
+
+ 0 - normal
+ 1 - audio
+ 2 - breathing
+
+ Normal: LEDs are fully on when enabled
+ Audio: LEDs brightness depends on sound level
+ Breathing: LEDs brightness varies at human breathing rate
\ No newline at end of file
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 7af9d0b..e2f4590 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -261,6 +261,20 @@ config HOLTEK_FF
Say Y here if you have a Holtek On Line Grip based game controller
and want to have force feedback support for it.

+config HID_GT683R
+ tristate "MSI GT68xR LED support"
+ depends on LEDS_CLASS && USB_HID
+ ---help---
+ Say Y here if you want to enable support for the three MSI GT68xR LEDs
+
+ This driver support following modes:
+ - Normal: LEDs are fully on when enabled
+ - Audio: LEDs brightness depends on sound level
+ - Breathing: LEDs brightness varies at human breathing rate
+
+ Currently the following devices are know to be supported:
+ - MSI GT683R
+
config HID_HUION
tristate "Huion tablets"
depends on USB_HID
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index fc712dd..7129311 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -48,6 +48,7 @@ obj-$(CONFIG_HID_EMS_FF) += hid-emsff.o
obj-$(CONFIG_HID_ELECOM) += hid-elecom.o
obj-$(CONFIG_HID_ELO) += hid-elo.o
obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o
+obj-$(CONFIG_HID_GT683R) += hid-gt683r.o
obj-$(CONFIG_HID_GYRATION) += hid-gyration.o
obj-$(CONFIG_HID_HOLTEK) += hid-holtek-kbd.o
obj-$(CONFIG_HID_HOLTEK) += hid-holtek-mouse.o
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index da52279..ec88fdb 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1827,6 +1827,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_OFFICE_KB) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) },
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_1) },
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_2) },
diff --git a/drivers/hid/hid-gt683r.c b/drivers/hid/hid-gt683r.c
new file mode 100644
index 0000000..5bd0113
--- /dev/null
+++ b/drivers/hid/hid-gt683r.c
@@ -0,0 +1,318 @@
+/*
+ * MSI GT683R led driver
+ *
+ * Copyright (c) 2014 Janne Kanniainen <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+#define GT683R_BUFFER_SIZE 8
+
+/*
+ * GT683R_LED_OFF: all LEDs are off
+ * GT683R_LED_AUDIO: LEDs brightness depends on sound level
+ * GT683R_LED_BREATHING: LEDs brightness varies at human breathing rate
+ * GT683R_LED_NORMAL: LEDs are fully on when enabled
+ */
+enum gt683r_led_mode {
+ GT683R_LED_OFF = 0,
+ GT683R_LED_AUDIO = 2,
+ GT683R_LED_BREATHING = 3,
+ GT683R_LED_NORMAL = 5
+};
+
+enum gt683r_panels {
+ GT683R_LED_BACK = 0,
+ GT683R_LED_SIDE = 1,
+ GT683R_LED_FRONT = 2,
+ GT683R_LED_COUNT,
+};
+
+static const char * const gt683r_panel_names[] = {
+ "back",
+ "side",
+ "front",
+};
+
+struct gt683r_led {
+ struct hid_device *hdev;
+ struct led_classdev led_devs[GT683R_LED_COUNT];
+ struct mutex lock;
+ struct work_struct work;
+ enum led_brightness brightnesses[GT683R_LED_COUNT];
+ enum gt683r_led_mode mode;
+};
+
+static const struct hid_device_id gt683r_led_id[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
+ { }
+};
+
+static void gt683r_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ int i;
+ struct device *dev = led_cdev->dev->parent;
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct gt683r_led *led = hid_get_drvdata(hdev);
+
+ for (i = 0; i < GT683R_LED_COUNT; i++) {
+ if (led_cdev == &led->led_devs[i])
+ break;
+ }
+
+ if (i < GT683R_LED_COUNT) {
+ led->brightnesses[i] = brightness;
+ schedule_work(&led->work);
+ }
+}
+
+static ssize_t leds_mode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ u8 sysfs_mode;
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct gt683r_led *led = hid_get_drvdata(hdev);
+
+ if (led->mode == GT683R_LED_NORMAL)
+ sysfs_mode = 0;
+ else if (led->mode == GT683R_LED_AUDIO)
+ sysfs_mode = 1;
+ else
+ sysfs_mode = 2;
+
+ return scnprintf(buf, PAGE_SIZE, "%u\n", sysfs_mode);
+}
+
+static ssize_t leds_mode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ u8 sysfs_mode;
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct gt683r_led *led = hid_get_drvdata(hdev);
+
+
+ if (kstrtou8(buf, 10, &sysfs_mode) || sysfs_mode > 2)
+ return -EINVAL;
+
+ mutex_lock(&led->lock);
+
+ if (sysfs_mode == 0)
+ led->mode = GT683R_LED_NORMAL;
+ else if (sysfs_mode == 1)
+ led->mode = GT683R_LED_AUDIO;
+ else
+ led->mode = GT683R_LED_BREATHING;
+
+ mutex_unlock(&led->lock);
+ schedule_work(&led->work);
+
+ return count;
+}
+
+static int gt683r_led_snd_msg(struct gt683r_led *led, u8 *msg)
+{
+ int ret;
+
+ ret = hid_hw_raw_request(led->hdev, msg[0], msg, GT683R_BUFFER_SIZE,
+ HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
+ if (ret != GT683R_BUFFER_SIZE) {
+ hid_err(led->hdev,
+ "failed to send set report request: %i\n", ret);
+ if (ret < 0)
+ return ret;
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int gt683r_leds_set(struct gt683r_led *led, u8 leds)
+{
+ int ret;
+ u8 *buffer;
+
+ buffer = kzalloc(GT683R_BUFFER_SIZE, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
+ buffer[0] = 0x01;
+ buffer[1] = 0x02;
+ buffer[2] = 0x30;
+ buffer[3] = leds;
+ ret = gt683r_led_snd_msg(led, buffer);
+
+ kfree(buffer);
+ return ret;
+}
+
+static int gt683r_mode_set(struct gt683r_led *led, u8 mode)
+{
+ int ret;
+ u8 *buffer;
+
+ buffer = kzalloc(GT683R_BUFFER_SIZE, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
+ buffer[0] = 0x01;
+ buffer[1] = 0x02;
+ buffer[2] = 0x20;
+ buffer[3] = mode;
+ buffer[4] = 0x01;
+ ret = gt683r_led_snd_msg(led, buffer);
+
+ kfree(buffer);
+ return ret;
+}
+
+static void gt683r_led_work(struct work_struct *work)
+{
+ int i;
+ u8 leds = 0;
+ u8 mode;
+ struct gt683r_led *led = container_of(work, struct gt683r_led, work);
+
+ mutex_lock(&led->lock);
+
+ for (i = 0; i < GT683R_LED_COUNT; i++) {
+ if (led->brightnesses[i])
+ leds |= BIT(i);
+ }
+
+ if (gt683r_leds_set(led, leds))
+ goto fail;
+
+ if (leds)
+ mode = led->mode;
+ else
+ mode = GT683R_LED_OFF;
+
+ gt683r_mode_set(led, mode);
+fail:
+ mutex_unlock(&led->lock);
+}
+
+static DEVICE_ATTR_RW(leds_mode);
+
+static struct attribute *gt683r_attributes[] = {
+ &dev_attr_leds_mode.attr,
+ NULL,
+};
+
+static struct attribute_group gt683r_attribute_group = {
+ .attrs = gt683r_attributes
+};
+
+static int gt683r_led_probe(struct hid_device *hdev,
+ const struct hid_device_id *id)
+{
+ int i;
+ int ret;
+ int name_sz;
+ char *name;
+ struct gt683r_led *led;
+
+ led = devm_kzalloc(&hdev->dev, sizeof(*led), GFP_KERNEL);
+ if (!led)
+ return -ENOMEM;
+
+ mutex_init(&led->lock);
+ INIT_WORK(&led->work, gt683r_led_work);
+
+ led->mode = GT683R_LED_NORMAL;
+ led->hdev = hdev;
+ hid_set_drvdata(hdev, led);
+
+ ret = hid_parse(hdev);
+ if (ret) {
+ hid_err(hdev, "hid parsing failed\n");
+ return ret;
+ }
+
+ ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
+ if (ret) {
+ hid_err(hdev, "hw start failed\n");
+ return ret;
+ }
+
+ for (i = 0; i < GT683R_LED_COUNT; i++) {
+ name_sz = strlen(dev_name(&hdev->dev)) +
+ strlen(gt683r_panel_names[i]) + 3;
+
+ name = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL);
+ if (!name) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ snprintf(name, name_sz, "%s::%s",
+ dev_name(&hdev->dev), gt683r_panel_names[i]);
+ led->led_devs[i].name = name;
+ led->led_devs[i].max_brightness = 1;
+ led->led_devs[i].brightness_set = gt683r_brightness_set;
+ ret = led_classdev_register(&hdev->dev, &led->led_devs[i]);
+ if (ret) {
+ hid_err(hdev, "could not register led device\n");
+ goto fail;
+ }
+ }
+
+ ret = sysfs_create_group(&led->hdev->dev.kobj, &gt683r_attribute_group);
+ if (ret) {
+ hid_err(hdev, "failed to create sysfs attributes\n");
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ for (i = i - 1; i >= 0; i--)
+ led_classdev_unregister(&led->led_devs[i]);
+ hid_hw_stop(hdev);
+ return ret;
+}
+
+static void gt683r_led_remove(struct hid_device *hdev)
+{
+ int i;
+ struct gt683r_led *led = hid_get_drvdata(hdev);
+
+ sysfs_remove_group(&led->hdev->dev.kobj, &gt683r_attribute_group);
+ for (i = 0; i < GT683R_LED_COUNT; i++)
+ led_classdev_unregister(&led->led_devs[i]);
+ flush_work(&led->work);
+ hid_hw_stop(hdev);
+}
+
+static struct hid_driver gt683r_led_driver = {
+ .probe = gt683r_led_probe,
+ .remove = gt683r_led_remove,
+ .name = "gt683r_led",
+ .id_table = gt683r_led_id,
+};
+
+module_hid_driver(gt683r_led_driver);
+
+MODULE_AUTHOR("Janne Kanniainen");
+MODULE_DESCRIPTION("MSI GT683R led driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 34bb220..3692d37 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -641,7 +641,7 @@
#define USB_DEVICE_ID_GENIUS_KB29E 0x3004

#define USB_VENDOR_ID_MSI 0x1770
-#define USB_DEVICE_ID_MSI_GX680R_LED_PANEL 0xff00
+#define USB_DEVICE_ID_MSI_GT683R_LED_PANEL 0xff00

#define USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR 0x0400
#define USB_DEVICE_ID_N_S_HARMONY 0xc359
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
index 8e4ddb3..c640e1d 100644
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -73,7 +73,7 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET },
- { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GX680R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
+ { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_NEXIO, USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_MOUSE, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NO_INIT_REPORTS },
--
1.9.2

2014-06-23 17:28:24

by Johan Hovold

[permalink] [raw]
Subject: Re: [PATCH v10] leds: USB: HID: Add support for MSI GT683R led panels

On Mon, Jun 23, 2014 at 08:16:48PM +0300, Janne Kanniainen wrote:
> This driver adds support for USB controlled led panels that exists in
> MSI GT683R laptop
>
> Signed-off-by: Janne Kanniainen <[email protected]>
> ---
> Changes in v2:
> - sorted headers to alphabetic order
> - using devm_kzalloc
> - using BIT(n)
> - using usb_control_msg instead of usb_submit_urb
> - removing unneeded code
> Changes in v3:
> - implemented as HID device
> - some cleanups and bug fixes
> Changes in v4:
> - more cleanups
> - support for selecting leds
> - suppport for selecting status
>
> Changes in v5:
> - mode attribute documented under Documentation/ABI
> - made array for led_classdev
> - led devices uses now recommended naming scheme
>
> Changes in v6:
> - flush_work added
> - using hid device name instead of hard coded gt683r
> - allocating name buffers with devm_kzalloc
>
> Changes in v7:
> - buf with for fixed
>
> Changes in v8:
> - some cleanups and bugs fixed
>
> Changes in v9:
> - few style issues fixed
>
> Changes in v10:
> - race condition fixed
> - using proper attribute group

You need to send a separate patch on top of v9, which Jiri has already
applied to his tree.

<snip>

> +static DEVICE_ATTR_RW(leds_mode);
> +
> +static struct attribute *gt683r_attributes[] = {
> + &dev_attr_leds_mode.attr,
> + NULL,
> +};
> +
> +static struct attribute_group gt683r_attribute_group = {
> + .attrs = gt683r_attributes
> +};
> +
> +static int gt683r_led_probe(struct hid_device *hdev,
> + const struct hid_device_id *id)
> +{
> + int i;
> + int ret;
> + int name_sz;
> + char *name;
> + struct gt683r_led *led;
> +
> + led = devm_kzalloc(&hdev->dev, sizeof(*led), GFP_KERNEL);
> + if (!led)
> + return -ENOMEM;
> +
> + mutex_init(&led->lock);
> + INIT_WORK(&led->work, gt683r_led_work);
> +
> + led->mode = GT683R_LED_NORMAL;
> + led->hdev = hdev;
> + hid_set_drvdata(hdev, led);
> +
> + ret = hid_parse(hdev);
> + if (ret) {
> + hid_err(hdev, "hid parsing failed\n");
> + return ret;
> + }
> +
> + ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
> + if (ret) {
> + hid_err(hdev, "hw start failed\n");
> + return ret;
> + }
> +
> + for (i = 0; i < GT683R_LED_COUNT; i++) {
> + name_sz = strlen(dev_name(&hdev->dev)) +
> + strlen(gt683r_panel_names[i]) + 3;
> +
> + name = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL);
> + if (!name) {
> + ret = -ENOMEM;
> + goto fail;
> + }
> +
> + snprintf(name, name_sz, "%s::%s",
> + dev_name(&hdev->dev), gt683r_panel_names[i]);
> + led->led_devs[i].name = name;
> + led->led_devs[i].max_brightness = 1;
> + led->led_devs[i].brightness_set = gt683r_brightness_set;
> + ret = led_classdev_register(&hdev->dev, &led->led_devs[i]);
> + if (ret) {
> + hid_err(hdev, "could not register led device\n");
> + goto fail;
> + }
> + }
> +
> + ret = sysfs_create_group(&led->hdev->dev.kobj, &gt683r_attribute_group);
> + if (ret) {
> + hid_err(hdev, "failed to create sysfs attributes\n");
> + goto fail;
> + }

This does not solve the race Greg is referring to. There's some more
info here:

http://kroah.com/log/blog/2013/06/26/how-to-create-a-sysfs-file-correctly/

but I'm not sure exactly how you'd apply that to an HID driver. Perhaps
you could use the struct device_driver in struct hid_driver (although it
is currently marked as "private").

Johan

> +
> + return 0;
> +
> +fail:
> + for (i = i - 1; i >= 0; i--)
> + led_classdev_unregister(&led->led_devs[i]);
> + hid_hw_stop(hdev);
> + return ret;
> +}
> +
> +static void gt683r_led_remove(struct hid_device *hdev)
> +{
> + int i;
> + struct gt683r_led *led = hid_get_drvdata(hdev);
> +
> + sysfs_remove_group(&led->hdev->dev.kobj, &gt683r_attribute_group);
> + for (i = 0; i < GT683R_LED_COUNT; i++)
> + led_classdev_unregister(&led->led_devs[i]);
> + flush_work(&led->work);
> + hid_hw_stop(hdev);
> +}
> +
> +static struct hid_driver gt683r_led_driver = {
> + .probe = gt683r_led_probe,
> + .remove = gt683r_led_remove,
> + .name = "gt683r_led",
> + .id_table = gt683r_led_id,
> +};
> +
> +module_hid_driver(gt683r_led_driver);
> +
> +MODULE_AUTHOR("Janne Kanniainen");
> +MODULE_DESCRIPTION("MSI GT683R led driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
> index 34bb220..3692d37 100644
> --- a/drivers/hid/hid-ids.h
> +++ b/drivers/hid/hid-ids.h
> @@ -641,7 +641,7 @@
> #define USB_DEVICE_ID_GENIUS_KB29E 0x3004
>
> #define USB_VENDOR_ID_MSI 0x1770
> -#define USB_DEVICE_ID_MSI_GX680R_LED_PANEL 0xff00
> +#define USB_DEVICE_ID_MSI_GT683R_LED_PANEL 0xff00
>
> #define USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR 0x0400
> #define USB_DEVICE_ID_N_S_HARMONY 0xc359
> diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
> index 8e4ddb3..c640e1d 100644
> --- a/drivers/hid/usbhid/hid-quirks.c
> +++ b/drivers/hid/usbhid/hid-quirks.c
> @@ -73,7 +73,7 @@ static const struct hid_blacklist {
> { USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS },
> { USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET },
> { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET },
> - { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GX680R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
> + { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
> { USB_VENDOR_ID_NEXIO, USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750, HID_QUIRK_NO_INIT_REPORTS },
> { USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_MOUSE, HID_QUIRK_NO_INIT_REPORTS },
> { USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NO_INIT_REPORTS },

2014-06-23 18:23:39

by Greg KH

[permalink] [raw]
Subject: Re: [PATCH v10] leds: USB: HID: Add support for MSI GT683R led panels

On Mon, Jun 23, 2014 at 08:16:48PM +0300, Janne Kanniainen wrote:
> + ret = sysfs_create_group(&led->hdev->dev.kobj, &gt683r_attribute_group);
> + if (ret) {
> + hid_err(hdev, "failed to create sysfs attributes\n");
> + goto fail;
> + }

No, you need to set the attribute group _before_ you call
led_classdev_register, as that is where the device will be created in
sysfs. Surely the other led drivers already do this? I'm almost afraid
to go look...

You also have to document your sysfs file in Documentation/ABI/

thanks,

greg k-h

2014-06-23 18:24:48

by Greg KH

[permalink] [raw]
Subject: Re: [PATCH v10] leds: USB: HID: Add support for MSI GT683R led panels

On Mon, Jun 23, 2014 at 02:23:24PM -0400, Greg KH wrote:
> On Mon, Jun 23, 2014 at 08:16:48PM +0300, Janne Kanniainen wrote:
> > + ret = sysfs_create_group(&led->hdev->dev.kobj, &gt683r_attribute_group);
> > + if (ret) {
> > + hid_err(hdev, "failed to create sysfs attributes\n");
> > + goto fail;
> > + }
>
> No, you need to set the attribute group _before_ you call
> led_classdev_register, as that is where the device will be created in
> sysfs. Surely the other led drivers already do this? I'm almost afraid
> to go look...

Yes, they do it already, set .dev_attr_group and you should be fine.

thanks,

greg k-h

2014-06-23 19:32:12

by Johan Hovold

[permalink] [raw]
Subject: Re: [PATCH v10] leds: USB: HID: Add support for MSI GT683R led panels

On Mon, Jun 23, 2014 at 02:24:32PM -0400, Greg KH wrote:
> On Mon, Jun 23, 2014 at 02:23:24PM -0400, Greg KH wrote:
> > On Mon, Jun 23, 2014 at 08:16:48PM +0300, Janne Kanniainen wrote:
> > > + ret = sysfs_create_group(&led->hdev->dev.kobj, &gt683r_attribute_group);
> > > + if (ret) {
> > > + hid_err(hdev, "failed to create sysfs attributes\n");
> > > + goto fail;
> > > + }
> >
> > No, you need to set the attribute group _before_ you call
> > led_classdev_register, as that is where the device will be created in
> > sysfs. Surely the other led drivers already do this? I'm almost afraid
> > to go look...
>
> Yes, they do it already, set .dev_attr_group and you should be fine.

But this isn't an attribute of the LEDs but rather of the parent HID
device that is being probed (the led_mode is common to all three LEDs
and thus belongs in the parent device, right?).

Johan

2014-06-23 19:41:14

by Greg KH

[permalink] [raw]
Subject: Re: [PATCH v10] leds: USB: HID: Add support for MSI GT683R led panels

On Mon, Jun 23, 2014 at 09:31:34PM +0200, Johan Hovold wrote:
> On Mon, Jun 23, 2014 at 02:24:32PM -0400, Greg KH wrote:
> > On Mon, Jun 23, 2014 at 02:23:24PM -0400, Greg KH wrote:
> > > On Mon, Jun 23, 2014 at 08:16:48PM +0300, Janne Kanniainen wrote:
> > > > + ret = sysfs_create_group(&led->hdev->dev.kobj, &gt683r_attribute_group);
> > > > + if (ret) {
> > > > + hid_err(hdev, "failed to create sysfs attributes\n");
> > > > + goto fail;
> > > > + }
> > >
> > > No, you need to set the attribute group _before_ you call
> > > led_classdev_register, as that is where the device will be created in
> > > sysfs. Surely the other led drivers already do this? I'm almost afraid
> > > to go look...
> >
> > Yes, they do it already, set .dev_attr_group and you should be fine.
>
> But this isn't an attribute of the LEDs but rather of the parent HID
> device that is being probed (the led_mode is common to all three LEDs
> and thus belongs in the parent device, right?).

Then that's even worse :(

The sysfs attribute should be on the class device here for the LED, you
should not put an attribute on a device you are not the driver for.

greg k-h

2014-06-23 19:52:50

by Johan Hovold

[permalink] [raw]
Subject: Re: [PATCH v10] leds: USB: HID: Add support for MSI GT683R led panels

On Mon, Jun 23, 2014 at 03:40:59PM -0400, Greg KH wrote:
> On Mon, Jun 23, 2014 at 09:31:34PM +0200, Johan Hovold wrote:
> > On Mon, Jun 23, 2014 at 02:24:32PM -0400, Greg KH wrote:
> > > On Mon, Jun 23, 2014 at 02:23:24PM -0400, Greg KH wrote:
> > > > On Mon, Jun 23, 2014 at 08:16:48PM +0300, Janne Kanniainen wrote:
> > > > > + ret = sysfs_create_group(&led->hdev->dev.kobj, &gt683r_attribute_group);
> > > > > + if (ret) {
> > > > > + hid_err(hdev, "failed to create sysfs attributes\n");
> > > > > + goto fail;
> > > > > + }
> > > >
> > > > No, you need to set the attribute group _before_ you call
> > > > led_classdev_register, as that is where the device will be created in
> > > > sysfs. Surely the other led drivers already do this? I'm almost afraid
> > > > to go look...
> > >
> > > Yes, they do it already, set .dev_attr_group and you should be fine.
> >
> > But this isn't an attribute of the LEDs but rather of the parent HID
> > device that is being probed (the led_mode is common to all three LEDs
> > and thus belongs in the parent device, right?).
>
> Then that's even worse :(
>
> The sysfs attribute should be on the class device here for the LED, you
> should not put an attribute on a device you are not the driver for.

But this is the driver for the HID device, which then in turn has three
individual LEDs. This particular device isn't really an input device
(the actual keyboard in this case appears to be connected over PS2), but
there are several HID drivers which are primarily input devices with
LEDs as sub-devices (e.g. drivers/hid/hid-lg4ff.c).

Johan

2014-06-23 20:25:04

by Greg KH

[permalink] [raw]
Subject: Re: [PATCH v10] leds: USB: HID: Add support for MSI GT683R led panels

On Mon, Jun 23, 2014 at 09:52:12PM +0200, Johan Hovold wrote:
> On Mon, Jun 23, 2014 at 03:40:59PM -0400, Greg KH wrote:
> > On Mon, Jun 23, 2014 at 09:31:34PM +0200, Johan Hovold wrote:
> > > On Mon, Jun 23, 2014 at 02:24:32PM -0400, Greg KH wrote:
> > > > On Mon, Jun 23, 2014 at 02:23:24PM -0400, Greg KH wrote:
> > > > > On Mon, Jun 23, 2014 at 08:16:48PM +0300, Janne Kanniainen wrote:
> > > > > > + ret = sysfs_create_group(&led->hdev->dev.kobj, &gt683r_attribute_group);
> > > > > > + if (ret) {
> > > > > > + hid_err(hdev, "failed to create sysfs attributes\n");
> > > > > > + goto fail;
> > > > > > + }
> > > > >
> > > > > No, you need to set the attribute group _before_ you call
> > > > > led_classdev_register, as that is where the device will be created in
> > > > > sysfs. Surely the other led drivers already do this? I'm almost afraid
> > > > > to go look...
> > > >
> > > > Yes, they do it already, set .dev_attr_group and you should be fine.
> > >
> > > But this isn't an attribute of the LEDs but rather of the parent HID
> > > device that is being probed (the led_mode is common to all three LEDs
> > > and thus belongs in the parent device, right?).
> >
> > Then that's even worse :(
> >
> > The sysfs attribute should be on the class device here for the LED, you
> > should not put an attribute on a device you are not the driver for.
>
> But this is the driver for the HID device, which then in turn has three
> individual LEDs. This particular device isn't really an input device
> (the actual keyboard in this case appears to be connected over PS2), but
> there are several HID drivers which are primarily input devices with
> LEDs as sub-devices (e.g. drivers/hid/hid-lg4ff.c).

I don't know the specifics here, but as you just created a class device,
why aren't the attributes on that class device? Shouldn't that be the
logical place for it, instead of having them on some "random" other type
of device? Userspace will never be notified that the attribute is on
that device due to the file being created "later", and tools using
libudev and the like will be looking at the LED class device, not the
"parent" device for any specific LED stuff.

but if this really is the way that all LED devices work, and userspace
programs are expecting this, that's seems odd.

greg k-h

2014-06-23 20:45:09

by Johan Hovold

[permalink] [raw]
Subject: Re: [PATCH v10] leds: USB: HID: Add support for MSI GT683R led panels

On Mon, Jun 23, 2014 at 04:24:48PM -0400, Greg KH wrote:
> On Mon, Jun 23, 2014 at 09:52:12PM +0200, Johan Hovold wrote:
> > On Mon, Jun 23, 2014 at 03:40:59PM -0400, Greg KH wrote:
> > > On Mon, Jun 23, 2014 at 09:31:34PM +0200, Johan Hovold wrote:
> > > > On Mon, Jun 23, 2014 at 02:24:32PM -0400, Greg KH wrote:
> > > > > On Mon, Jun 23, 2014 at 02:23:24PM -0400, Greg KH wrote:
> > > > > > On Mon, Jun 23, 2014 at 08:16:48PM +0300, Janne Kanniainen wrote:
> > > > > > > + ret = sysfs_create_group(&led->hdev->dev.kobj, &gt683r_attribute_group);
> > > > > > > + if (ret) {
> > > > > > > + hid_err(hdev, "failed to create sysfs attributes\n");
> > > > > > > + goto fail;
> > > > > > > + }
> > > > > >
> > > > > > No, you need to set the attribute group _before_ you call
> > > > > > led_classdev_register, as that is where the device will be created in
> > > > > > sysfs. Surely the other led drivers already do this? I'm almost afraid
> > > > > > to go look...
> > > > >
> > > > > Yes, they do it already, set .dev_attr_group and you should be fine.
> > > >
> > > > But this isn't an attribute of the LEDs but rather of the parent HID
> > > > device that is being probed (the led_mode is common to all three LEDs
> > > > and thus belongs in the parent device, right?).
> > >
> > > Then that's even worse :(
> > >
> > > The sysfs attribute should be on the class device here for the LED, you
> > > should not put an attribute on a device you are not the driver for.
> >
> > But this is the driver for the HID device, which then in turn has three
> > individual LEDs. This particular device isn't really an input device
> > (the actual keyboard in this case appears to be connected over PS2), but
> > there are several HID drivers which are primarily input devices with
> > LEDs as sub-devices (e.g. drivers/hid/hid-lg4ff.c).
>
> I don't know the specifics here, but as you just created a class device,
> why aren't the attributes on that class device? Shouldn't that be the
> logical place for it, instead of having them on some "random" other type
> of device?

This is a non-standard attribute of this particular laptop. It has three
individual LEDs that can be enabled separately (using standard LED class
attributes), but they will all three be in the same "mode" (which here
apparently means that they can be fully on, vary with the volume(?!), or
pulse synchronously when enabled).

If we were to implement this mode attribute as a class attribute,
changing the mode of of one LED would also change the mode of the other
two devices (LEDs). Therefore I think it has to be an attribute of the
parent HID device (that the driver is for).

> Userspace will never be notified that the attribute is on
> that device due to the file being created "later", and tools using
> libudev and the like will be looking at the LED class device, not the
> "parent" device for any specific LED stuff.

Yeah, that seems to be the case. I doubt anyone will care much about
this particular custom attribute, but sure, setting the led_mode of the
HID device from an udev rule could be problematic.

> but if this really is the way that all LED devices work, and userspace
> programs are expecting this, that's seems odd.

Johan

2014-06-24 13:11:28

by Bjørn Mork

[permalink] [raw]
Subject: Re: [PATCH v10] leds: USB: HID: Add support for MSI GT683R led panels

Johan Hovold <[email protected]> writes:

> This is a non-standard attribute of this particular laptop. It has three
> individual LEDs that can be enabled separately (using standard LED class
> attributes), but they will all three be in the same "mode" (which here
> apparently means that they can be fully on, vary with the volume(?!), or
> pulse synchronously when enabled).
>
> If we were to implement this mode attribute as a class attribute,
> changing the mode of of one LED would also change the mode of the other
> two devices (LEDs).

Document this behaviour and it becomes a feature.


Bjørn

2014-06-24 14:50:42

by Johan Hovold

[permalink] [raw]
Subject: Re: [PATCH v10] leds: USB: HID: Add support for MSI GT683R led panels

On Tue, Jun 24, 2014 at 03:10:43PM +0200, Bj?rn Mork wrote:
> Johan Hovold <[email protected]> writes:
>
> > This is a non-standard attribute of this particular laptop. It has three
> > individual LEDs that can be enabled separately (using standard LED class
> > attributes), but they will all three be in the same "mode" (which here
> > apparently means that they can be fully on, vary with the volume(?!), or
> > pulse synchronously when enabled).
> >
> > If we were to implement this mode attribute as a class attribute,
> > changing the mode of of one LED would also change the mode of the other
> > two devices (LEDs).
>
> Document this behaviour and it becomes a feature.

Yeah, you're right. That is probably the best way to handle this.

Janne, could you send two separate patches on top of v9 that fixes the
use-before-initialisation (reported by Oliver) and moves the led_mode
attribute from the parent HID device to the led-class devices (thereby
fixing the race reported by Greg)?

As the "led_"-prefix will then be redundant, you should probably rename
the attribute as "msi_mode" or similar (just "mode" might be too
generic).

Remember to update the ABI documentation as well, including mentioning
the fact that changing the mode of one LED will update the mode of its
two sibling devices as well (as suggested by Bj?rn).

Thanks,
Johan

2014-06-24 19:39:53

by Janne Kanniainen

[permalink] [raw]
Subject: [PATCH 1/2] HID: leds: fix race condition in MSI GT683R driver

This will fix race condition noticed by Oliver Neukum. Sysfs files are created
before mutex and work are initialized.

Signed-off-by: Janne Kanniainen <[email protected]>
---
drivers/hid/hid-gt683r.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/hid/hid-gt683r.c b/drivers/hid/hid-gt683r.c
index 077f7a1..073bd80 100644
--- a/drivers/hid/hid-gt683r.c
+++ b/drivers/hid/hid-gt683r.c
@@ -227,6 +227,9 @@ static int gt683r_led_probe(struct hid_device *hdev,
if (!led)
return -ENOMEM;

+ mutex_init(&led->lock);
+ INIT_WORK(&led->work, gt683r_led_work);
+
led->mode = GT683R_LED_NORMAL;
led->hdev = hdev;
hid_set_drvdata(hdev, led);
@@ -271,9 +274,6 @@ static int gt683r_led_probe(struct hid_device *hdev,
goto fail;
}

- mutex_init(&led->lock);
- INIT_WORK(&led->work, gt683r_led_work);
-
return 0;

fail:
--
1.9.2

2014-06-24 19:39:59

by Janne Kanniainen

[permalink] [raw]
Subject: [PATCH 2/2] HID: leds: move led_mode attribute to led-class devices in MSI GT683R driver

Move led_mode attribute from HID device to led-class devices and rename it
msi_mode.

Signed-off-by: Janne Kanniainen <[email protected]>
---
.../ABI/testing/sysfs-class-hid-driver-gt683r | 6 ++-
drivers/hid/hid-gt683r.c | 50 +++++++++++++++++-----
2 files changed, 43 insertions(+), 13 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
index 317e9d5..c97970a 100644
--- a/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
+++ b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
@@ -1,9 +1,11 @@
-What: /sys/class/hidraw/<hidraw>/device/leds_mode
+What: /sys/class/leds/<led>/msi_mode
Date: Jun 2014
KernelVersion: 3.17
Contact: Janne Kanniainen <[email protected]>
Description:
- Set the mode of LEDs
+ Set the mode of LEDs. You should notice that changing the mode
+ of one LED will update the mode of its two sibling devices as
+ well

0 - normal
1 - audio
diff --git a/drivers/hid/hid-gt683r.c b/drivers/hid/hid-gt683r.c
index 073bd80..1c942eb 100644
--- a/drivers/hid/hid-gt683r.c
+++ b/drivers/hid/hid-gt683r.c
@@ -58,6 +58,7 @@ struct gt683r_led {
struct work_struct work;
enum led_brightness brightnesses[GT683R_LED_COUNT];
enum gt683r_led_mode mode;
+ const struct attribute_group *dev_attr_group[GT683R_LED_COUNT];
};

static const struct hid_device_id gt683r_led_id[] = {
@@ -84,12 +85,13 @@ static void gt683r_brightness_set(struct led_classdev *led_cdev,
}
}

-static ssize_t leds_mode_show(struct device *dev,
+static ssize_t msi_mode_show(struct device *led_dev,
struct device_attribute *attr,
char *buf)
{
u8 sysfs_mode;
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = container_of(led_dev->parent,
+ struct hid_device, dev);
struct gt683r_led *led = hid_get_drvdata(hdev);

if (led->mode == GT683R_LED_NORMAL)
@@ -102,15 +104,15 @@ static ssize_t leds_mode_show(struct device *dev,
return scnprintf(buf, PAGE_SIZE, "%u\n", sysfs_mode);
}

-static ssize_t leds_mode_store(struct device *dev,
+static ssize_t msi_mode_store(struct device *led_dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
u8 sysfs_mode;
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = container_of(led_dev->parent,
+ struct hid_device, dev);
struct gt683r_led *led = hid_get_drvdata(hdev);

-
if (kstrtou8(buf, 10, &sysfs_mode) || sysfs_mode > 2)
return -EINVAL;

@@ -212,7 +214,16 @@ fail:
mutex_unlock(&led->lock);
}

-static DEVICE_ATTR_RW(leds_mode);
+static DEVICE_ATTR_RW(msi_mode);
+
+static struct attribute *gt683r_attributes[] = {
+ &dev_attr_msi_mode.attr,
+ NULL
+};
+
+static const struct attribute_group gt683r_group = {
+ .attrs = gt683r_attributes
+};

static int gt683r_led_probe(struct hid_device *hdev,
const struct hid_device_id *id)
@@ -261,6 +272,7 @@ static int gt683r_led_probe(struct hid_device *hdev,
led->led_devs[i].name = name;
led->led_devs[i].max_brightness = 1;
led->led_devs[i].brightness_set = gt683r_brightness_set;
+
ret = led_classdev_register(&hdev->dev, &led->led_devs[i]);
if (ret) {
hid_err(hdev, "could not register led device\n");
@@ -268,17 +280,29 @@ static int gt683r_led_probe(struct hid_device *hdev,
}
}

- ret = device_create_file(&led->hdev->dev, &dev_attr_leds_mode);
- if (ret) {
- hid_err(hdev, "could not make mode attribute file\n");
- goto fail;
+ for (i = 0; i < GT683R_LED_COUNT; i++) {
+ led->dev_attr_group[i] = &gt683r_group;
+
+ ret = sysfs_create_group(&led->led_devs[i].dev->kobj,
+ led->dev_attr_group[i]);
+ if (ret) {
+ hid_err(hdev, "could not create attribute file\n");
+ goto sysfs_create_fail;
+ }
}

return 0;

+sysfs_create_fail:
+ for (i = i - 1; i >= 0; i--)
+ sysfs_remove_group(&led->led_devs[i].dev->kobj,
+ led->dev_attr_group[i]);
+
+ i = GT683R_LED_COUNT;
fail:
for (i = i - 1; i >= 0; i--)
led_classdev_unregister(&led->led_devs[i]);
+
hid_hw_stop(hdev);
return ret;
}
@@ -288,9 +312,13 @@ static void gt683r_led_remove(struct hid_device *hdev)
int i;
struct gt683r_led *led = hid_get_drvdata(hdev);

- device_remove_file(&hdev->dev, &dev_attr_leds_mode);
+ for (i = 0; i < GT683R_LED_COUNT; i++)
+ sysfs_remove_group(&led->led_devs[i].dev->kobj,
+ led->dev_attr_group[i]);
+
for (i = 0; i < GT683R_LED_COUNT; i++)
led_classdev_unregister(&led->led_devs[i]);
+
flush_work(&led->work);
hid_hw_stop(hdev);
}
--
1.9.2

2014-06-24 19:57:05

by Greg KH

[permalink] [raw]
Subject: Re: [PATCH 2/2] HID: leds: move led_mode attribute to led-class devices in MSI GT683R driver

On Tue, Jun 24, 2014 at 10:38:38PM +0300, Janne Kanniainen wrote:
> Move led_mode attribute from HID device to led-class devices and rename it
> msi_mode.
>
> Signed-off-by: Janne Kanniainen <[email protected]>
> ---
> .../ABI/testing/sysfs-class-hid-driver-gt683r | 6 ++-
> drivers/hid/hid-gt683r.c | 50 +++++++++++++++++-----
> 2 files changed, 43 insertions(+), 13 deletions(-)
>
> diff --git a/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
> index 317e9d5..c97970a 100644
> --- a/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
> +++ b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
> @@ -1,9 +1,11 @@
> -What: /sys/class/hidraw/<hidraw>/device/leds_mode
> +What: /sys/class/leds/<led>/msi_mode
> Date: Jun 2014
> KernelVersion: 3.17
> Contact: Janne Kanniainen <[email protected]>
> Description:
> - Set the mode of LEDs
> + Set the mode of LEDs. You should notice that changing the mode
> + of one LED will update the mode of its two sibling devices as
> + well
>
> 0 - normal
> 1 - audio
> diff --git a/drivers/hid/hid-gt683r.c b/drivers/hid/hid-gt683r.c
> index 073bd80..1c942eb 100644
> --- a/drivers/hid/hid-gt683r.c
> +++ b/drivers/hid/hid-gt683r.c
> @@ -58,6 +58,7 @@ struct gt683r_led {
> struct work_struct work;
> enum led_brightness brightnesses[GT683R_LED_COUNT];
> enum gt683r_led_mode mode;
> + const struct attribute_group *dev_attr_group[GT683R_LED_COUNT];
> };
>
> static const struct hid_device_id gt683r_led_id[] = {
> @@ -84,12 +85,13 @@ static void gt683r_brightness_set(struct led_classdev *led_cdev,
> }
> }
>
> -static ssize_t leds_mode_show(struct device *dev,
> +static ssize_t msi_mode_show(struct device *led_dev,
> struct device_attribute *attr,
> char *buf)
> {
> u8 sysfs_mode;
> - struct hid_device *hdev = container_of(dev, struct hid_device, dev);
> + struct hid_device *hdev = container_of(led_dev->parent,
> + struct hid_device, dev);
> struct gt683r_led *led = hid_get_drvdata(hdev);
>
> if (led->mode == GT683R_LED_NORMAL)
> @@ -102,15 +104,15 @@ static ssize_t leds_mode_show(struct device *dev,
> return scnprintf(buf, PAGE_SIZE, "%u\n", sysfs_mode);
> }
>
> -static ssize_t leds_mode_store(struct device *dev,
> +static ssize_t msi_mode_store(struct device *led_dev,
> struct device_attribute *attr,
> const char *buf, size_t count)
> {
> u8 sysfs_mode;
> - struct hid_device *hdev = container_of(dev, struct hid_device, dev);
> + struct hid_device *hdev = container_of(led_dev->parent,
> + struct hid_device, dev);
> struct gt683r_led *led = hid_get_drvdata(hdev);
>
> -
> if (kstrtou8(buf, 10, &sysfs_mode) || sysfs_mode > 2)
> return -EINVAL;
>
> @@ -212,7 +214,16 @@ fail:
> mutex_unlock(&led->lock);
> }
>
> -static DEVICE_ATTR_RW(leds_mode);
> +static DEVICE_ATTR_RW(msi_mode);
> +
> +static struct attribute *gt683r_attributes[] = {
> + &dev_attr_msi_mode.attr,
> + NULL
> +};
> +
> +static const struct attribute_group gt683r_group = {
> + .attrs = gt683r_attributes
> +};
>
> static int gt683r_led_probe(struct hid_device *hdev,
> const struct hid_device_id *id)
> @@ -261,6 +272,7 @@ static int gt683r_led_probe(struct hid_device *hdev,
> led->led_devs[i].name = name;
> led->led_devs[i].max_brightness = 1;
> led->led_devs[i].brightness_set = gt683r_brightness_set;
> +
> ret = led_classdev_register(&hdev->dev, &led->led_devs[i]);
> if (ret) {
> hid_err(hdev, "could not register led device\n");
> @@ -268,17 +280,29 @@ static int gt683r_led_probe(struct hid_device *hdev,
> }
> }
>
> - ret = device_create_file(&led->hdev->dev, &dev_attr_leds_mode);
> - if (ret) {
> - hid_err(hdev, "could not make mode attribute file\n");
> - goto fail;
> + for (i = 0; i < GT683R_LED_COUNT; i++) {
> + led->dev_attr_group[i] = &gt683r_group;
> +
> + ret = sysfs_create_group(&led->led_devs[i].dev->kobj,
> + led->dev_attr_group[i]);

why not use sysfs_create_groups()?

And why are you doing it this way, the led device should have the
attribute group and it should be created automatically by the driver
core, no driver should need to create it like you are doing so here.

thanks,

greg k-h

2014-06-25 11:56:20

by Johan Hovold

[permalink] [raw]
Subject: Re: [PATCH 2/2] HID: leds: move led_mode attribute to led-class devices in MSI GT683R driver

On Tue, Jun 24, 2014 at 03:56:51PM -0400, Greg KH wrote:
> On Tue, Jun 24, 2014 at 10:38:38PM +0300, Janne Kanniainen wrote:

> > static int gt683r_led_probe(struct hid_device *hdev,
> > const struct hid_device_id *id)
> > @@ -261,6 +272,7 @@ static int gt683r_led_probe(struct hid_device *hdev,
> > led->led_devs[i].name = name;
> > led->led_devs[i].max_brightness = 1;
> > led->led_devs[i].brightness_set = gt683r_brightness_set;
> > +
> > ret = led_classdev_register(&hdev->dev, &led->led_devs[i]);
> > if (ret) {
> > hid_err(hdev, "could not register led device\n");
> > @@ -268,17 +280,29 @@ static int gt683r_led_probe(struct hid_device *hdev,
> > }
> > }
> >
> > - ret = device_create_file(&led->hdev->dev, &dev_attr_leds_mode);
> > - if (ret) {
> > - hid_err(hdev, "could not make mode attribute file\n");
> > - goto fail;
> > + for (i = 0; i < GT683R_LED_COUNT; i++) {
> > + led->dev_attr_group[i] = &gt683r_group;
> > +
> > + ret = sysfs_create_group(&led->led_devs[i].dev->kobj,
> > + led->dev_attr_group[i]);
>
> why not use sysfs_create_groups()?
>
> And why are you doing it this way, the led device should have the
> attribute group and it should be created automatically by the driver
> core, no driver should need to create it like you are doing so here.

Turns out it was easier said than done when I asked Janne to move the
attribute. And all led drivers with custom attributes currently suffer
from this race.

Janne, you'll need to apply patch below and then set the groups field of
the led_devs before registering them. You can use the ATTRIBUTE_GROUPS
macro in the following way:

static struct attribute *gt683r_led_attrs[] = {
&dev_attr_msi_mode.attr,
NULL,
};
ATTRIBUTE_GROUPS(gt683r_led);

and then set .groups to gt683r_led_groups.

I have started fixing some of the other led drivers and I'll try to
submit a series (including the below patch) later today.

Thanks,
Johan


>From 226f1de5e094f2ec99f45d486652505ef915af73 Mon Sep 17 00:00:00 2001
From: Johan Hovold <[email protected]>
Date: Wed, 25 Jun 2014 12:33:18 +0200
Subject: [PATCH] leds: add led-class attribute-group support

Allow led-class devices to be created with optional attribute groups.

This is needed in order to allow led drivers to create custom device
attributes in a race-free manner.

Signed-off-by: Johan Hovold <[email protected]>
---
drivers/leds/led-class.c | 5 +++--
include/linux/leds.h | 2 ++
2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index f37d63cf726b..aa29198fca3e 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -210,8 +210,9 @@ static const struct dev_pm_ops leds_class_dev_pm_ops = {
*/
int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
{
- led_cdev->dev = device_create(leds_class, parent, 0, led_cdev,
- "%s", led_cdev->name);
+ led_cdev->dev = device_create_with_groups(leds_class, parent, 0,
+ led_cdev, led_cdev->groups,
+ "%s", led_cdev->name);
if (IS_ERR(led_cdev->dev))
return PTR_ERR(led_cdev->dev);

diff --git a/include/linux/leds.h b/include/linux/leds.h
index 0287ab296689..e43686472197 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -63,6 +63,8 @@ struct led_classdev {
unsigned long *delay_off);

struct device *dev;
+ const struct attribute_group **groups;
+
struct list_head node; /* LED Device list */
const char *default_trigger; /* Trigger to use */

--
1.8.5.5

2014-06-25 15:59:43

by Janne Kanniainen

[permalink] [raw]
Subject: [PATCH 2/2 v2] HID: leds: Use attribute-groups in MSI GT683R driver

Use attribute-groups to fix race condition.

Signed-off-by: Janne Kanniainen <[email protected]>
---
.../ABI/testing/sysfs-class-hid-driver-gt683r | 6 +++--
drivers/hid/hid-gt683r.c | 31 +++++++++++++---------
2 files changed, 22 insertions(+), 15 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
index 317e9d5..c97970a 100644
--- a/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
+++ b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
@@ -1,9 +1,11 @@
-What: /sys/class/hidraw/<hidraw>/device/leds_mode
+What: /sys/class/leds/<led>/msi_mode
Date: Jun 2014
KernelVersion: 3.17
Contact: Janne Kanniainen <[email protected]>
Description:
- Set the mode of LEDs
+ Set the mode of LEDs. You should notice that changing the mode
+ of one LED will update the mode of its two sibling devices as
+ well

0 - normal
1 - audio
diff --git a/drivers/hid/hid-gt683r.c b/drivers/hid/hid-gt683r.c
index 073bd80..aba6636 100644
--- a/drivers/hid/hid-gt683r.c
+++ b/drivers/hid/hid-gt683r.c
@@ -84,12 +84,13 @@ static void gt683r_brightness_set(struct led_classdev *led_cdev,
}
}

-static ssize_t leds_mode_show(struct device *dev,
+static ssize_t msi_mode_show(struct device *led_dev,
struct device_attribute *attr,
char *buf)
{
u8 sysfs_mode;
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = container_of(led_dev->parent,
+ struct hid_device, dev);
struct gt683r_led *led = hid_get_drvdata(hdev);

if (led->mode == GT683R_LED_NORMAL)
@@ -102,15 +103,15 @@ static ssize_t leds_mode_show(struct device *dev,
return scnprintf(buf, PAGE_SIZE, "%u\n", sysfs_mode);
}

-static ssize_t leds_mode_store(struct device *dev,
+static ssize_t msi_mode_store(struct device *led_dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
u8 sysfs_mode;
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = container_of(led_dev->parent,
+ struct hid_device, dev);
struct gt683r_led *led = hid_get_drvdata(hdev);

-
if (kstrtou8(buf, 10, &sysfs_mode) || sysfs_mode > 2)
return -EINVAL;

@@ -212,7 +213,14 @@ fail:
mutex_unlock(&led->lock);
}

-static DEVICE_ATTR_RW(leds_mode);
+static DEVICE_ATTR_RW(msi_mode);
+
+static struct attribute *gt683r_led_attrs[] = {
+ &dev_attr_msi_mode.attr,
+ NULL
+};
+
+ATTRIBUTE_GROUPS(gt683r_led);

static int gt683r_led_probe(struct hid_device *hdev,
const struct hid_device_id *id)
@@ -261,6 +269,8 @@ static int gt683r_led_probe(struct hid_device *hdev,
led->led_devs[i].name = name;
led->led_devs[i].max_brightness = 1;
led->led_devs[i].brightness_set = gt683r_brightness_set;
+ led->led_devs[i].groups = gt683r_led_groups;
+
ret = led_classdev_register(&hdev->dev, &led->led_devs[i]);
if (ret) {
hid_err(hdev, "could not register led device\n");
@@ -268,17 +278,12 @@ static int gt683r_led_probe(struct hid_device *hdev,
}
}

- ret = device_create_file(&led->hdev->dev, &dev_attr_leds_mode);
- if (ret) {
- hid_err(hdev, "could not make mode attribute file\n");
- goto fail;
- }
-
return 0;

fail:
for (i = i - 1; i >= 0; i--)
led_classdev_unregister(&led->led_devs[i]);
+
hid_hw_stop(hdev);
return ret;
}
@@ -288,9 +293,9 @@ static void gt683r_led_remove(struct hid_device *hdev)
int i;
struct gt683r_led *led = hid_get_drvdata(hdev);

- device_remove_file(&hdev->dev, &dev_attr_leds_mode);
for (i = 0; i < GT683R_LED_COUNT; i++)
led_classdev_unregister(&led->led_devs[i]);
+
flush_work(&led->work);
hid_hw_stop(hdev);
}
--
1.9.2

2014-06-25 17:41:45

by Johan Hovold

[permalink] [raw]
Subject: Re: [PATCH 2/2 v2] HID: leds: Use attribute-groups in MSI GT683R driver

On Wed, Jun 25, 2014 at 06:59:26PM +0300, Janne Kanniainen wrote:
> Use attribute-groups to fix race condition.

The primary thing you're doing here is moving and renaming the attribute
to the led class devices, so you need to mention that (and update the
patch subject). But you can mention the race as well.

> Signed-off-by: Janne Kanniainen <[email protected]>
> ---
> .../ABI/testing/sysfs-class-hid-driver-gt683r | 6 +++--
> drivers/hid/hid-gt683r.c | 31 +++++++++++++---------
> 2 files changed, 22 insertions(+), 15 deletions(-)
>
> diff --git a/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
> index 317e9d5..c97970a 100644
> --- a/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
> +++ b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r

I think you need to rename this file as well to
sysfs-class-leds-driver-gt683r (use git mv).

> @@ -1,9 +1,11 @@
> -What: /sys/class/hidraw/<hidraw>/device/leds_mode
> +What: /sys/class/leds/<led>/msi_mode
> Date: Jun 2014
> KernelVersion: 3.17
> Contact: Janne Kanniainen <[email protected]>
> Description:
> - Set the mode of LEDs
> + Set the mode of LEDs. You should notice that changing the mode
> + of one LED will update the mode of its two sibling devices as
> + well

Missing period ('.').

>
> 0 - normal
> 1 - audio
> diff --git a/drivers/hid/hid-gt683r.c b/drivers/hid/hid-gt683r.c
> index 073bd80..aba6636 100644
> --- a/drivers/hid/hid-gt683r.c
> +++ b/drivers/hid/hid-gt683r.c
> @@ -84,12 +84,13 @@ static void gt683r_brightness_set(struct led_classdev *led_cdev,
> }
> }
>
> -static ssize_t leds_mode_show(struct device *dev,
> +static ssize_t msi_mode_show(struct device *led_dev,

No need to rename dev.

> struct device_attribute *attr,
> char *buf)
> {
> u8 sysfs_mode;
> - struct hid_device *hdev = container_of(dev, struct hid_device, dev);
> + struct hid_device *hdev = container_of(led_dev->parent,
> + struct hid_device, dev);
> struct gt683r_led *led = hid_get_drvdata(hdev);
>
> if (led->mode == GT683R_LED_NORMAL)
> @@ -102,15 +103,15 @@ static ssize_t leds_mode_show(struct device *dev,
> return scnprintf(buf, PAGE_SIZE, "%u\n", sysfs_mode);
> }
>
> -static ssize_t leds_mode_store(struct device *dev,
> +static ssize_t msi_mode_store(struct device *led_dev,

Same here.

> struct device_attribute *attr,
> const char *buf, size_t count)
> {
> u8 sysfs_mode;
> - struct hid_device *hdev = container_of(dev, struct hid_device, dev);
> + struct hid_device *hdev = container_of(led_dev->parent,
> + struct hid_device, dev);
> struct gt683r_led *led = hid_get_drvdata(hdev);
>
> -

You should not include random whitespace fixes in your patches (there
are two more below). If needed you can do that in a separate patch.

> if (kstrtou8(buf, 10, &sysfs_mode) || sysfs_mode > 2)
> return -EINVAL;
>
> @@ -212,7 +213,14 @@ fail:
> mutex_unlock(&led->lock);
> }
>
> -static DEVICE_ATTR_RW(leds_mode);
> +static DEVICE_ATTR_RW(msi_mode);
> +
> +static struct attribute *gt683r_led_attrs[] = {
> + &dev_attr_msi_mode.attr,
> + NULL
> +};
> +
> +ATTRIBUTE_GROUPS(gt683r_led);
>
> static int gt683r_led_probe(struct hid_device *hdev,
> const struct hid_device_id *id)
> @@ -261,6 +269,8 @@ static int gt683r_led_probe(struct hid_device *hdev,
> led->led_devs[i].name = name;
> led->led_devs[i].max_brightness = 1;
> led->led_devs[i].brightness_set = gt683r_brightness_set;
> + led->led_devs[i].groups = gt683r_led_groups;
> +

Great. This fixes the race with userspace.

Did you see the attribute-race series I posted? Not sure how best to
handle the dependency, as those patches should probably go in through
the LEDs tree, while the first patch in that series (adding the groups
field) is a dependency for this patch.

Jiri, how would this best be solved?

Thanks,
Johan

> ret = led_classdev_register(&hdev->dev, &led->led_devs[i]);
> if (ret) {
> hid_err(hdev, "could not register led device\n");
> @@ -268,17 +278,12 @@ static int gt683r_led_probe(struct hid_device *hdev,
> }
> }
>
> - ret = device_create_file(&led->hdev->dev, &dev_attr_leds_mode);
> - if (ret) {
> - hid_err(hdev, "could not make mode attribute file\n");
> - goto fail;
> - }
> -
> return 0;
>
> fail:
> for (i = i - 1; i >= 0; i--)
> led_classdev_unregister(&led->led_devs[i]);
> +
> hid_hw_stop(hdev);
> return ret;
> }
> @@ -288,9 +293,9 @@ static void gt683r_led_remove(struct hid_device *hdev)
> int i;
> struct gt683r_led *led = hid_get_drvdata(hdev);
>
> - device_remove_file(&hdev->dev, &dev_attr_leds_mode);
> for (i = 0; i < GT683R_LED_COUNT; i++)
> led_classdev_unregister(&led->led_devs[i]);
> +
> flush_work(&led->work);
> hid_hw_stop(hdev);
> }

2014-06-25 18:13:29

by Janne Kanniainen

[permalink] [raw]
Subject: [PATCH 2/2 v3] HID: leds: move led_mode attribute to led-class devices in MSI GT683R driver

Move led_mode attribute from HID device to led-class devices and rename it
msi_mode. This will also fix race condition by using attribute-groups.

Signed-off-by: Janne Kanniainen <[email protected]>
---

Changes in v3:
- Style fixes
- Rename sysfs-class-hid-driver-gt683r to sysfs-class-leds-driver-gt683r

.../ABI/testing/sysfs-class-hid-driver-gt683r | 14 -----------
.../ABI/testing/sysfs-class-leds-driver-gt683r | 16 +++++++++++++
drivers/hid/hid-gt683r.c | 28 ++++++++++++----------
3 files changed, 32 insertions(+), 26 deletions(-)
delete mode 100644 Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
create mode 100644 Documentation/ABI/testing/sysfs-class-leds-driver-gt683r

diff --git a/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
deleted file mode 100644
index 317e9d5..0000000
--- a/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
+++ /dev/null
@@ -1,14 +0,0 @@
-What: /sys/class/hidraw/<hidraw>/device/leds_mode
-Date: Jun 2014
-KernelVersion: 3.17
-Contact: Janne Kanniainen <[email protected]>
-Description:
- Set the mode of LEDs
-
- 0 - normal
- 1 - audio
- 2 - breathing
-
- Normal: LEDs are fully on when enabled
- Audio: LEDs brightness depends on sound level
- Breathing: LEDs brightness varies at human breathing rate
\ No newline at end of file
diff --git a/Documentation/ABI/testing/sysfs-class-leds-driver-gt683r b/Documentation/ABI/testing/sysfs-class-leds-driver-gt683r
new file mode 100644
index 0000000..29769fb
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-leds-driver-gt683r
@@ -0,0 +1,16 @@
+What: /sys/class/leds/<led>/msi_mode
+Date: Jun 2014
+KernelVersion: 3.17
+Contact: Janne Kanniainen <[email protected]>
+Description:
+ Set the mode of LEDs. You should notice that changing the mode
+ of one LED will update the mode of its two sibling devices as
+ well.
+
+ 0 - normal
+ 1 - audio
+ 2 - breathing
+
+ Normal: LEDs are fully on when enabled
+ Audio: LEDs brightness depends on sound level
+ Breathing: LEDs brightness varies at human breathing rate
\ No newline at end of file
diff --git a/drivers/hid/hid-gt683r.c b/drivers/hid/hid-gt683r.c
index 073bd80..ba27ee1 100644
--- a/drivers/hid/hid-gt683r.c
+++ b/drivers/hid/hid-gt683r.c
@@ -84,12 +84,13 @@ static void gt683r_brightness_set(struct led_classdev *led_cdev,
}
}

-static ssize_t leds_mode_show(struct device *dev,
+static ssize_t msi_mode_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
u8 sysfs_mode;
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = container_of(dev->parent,
+ struct hid_device, dev);
struct gt683r_led *led = hid_get_drvdata(hdev);

if (led->mode == GT683R_LED_NORMAL)
@@ -102,12 +103,13 @@ static ssize_t leds_mode_show(struct device *dev,
return scnprintf(buf, PAGE_SIZE, "%u\n", sysfs_mode);
}

-static ssize_t leds_mode_store(struct device *dev,
+static ssize_t msi_mode_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
u8 sysfs_mode;
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = container_of(dev->parent,
+ struct hid_device, dev);
struct gt683r_led *led = hid_get_drvdata(hdev);


@@ -212,7 +214,14 @@ fail:
mutex_unlock(&led->lock);
}

-static DEVICE_ATTR_RW(leds_mode);
+static DEVICE_ATTR_RW(msi_mode);
+
+static struct attribute *gt683r_led_attrs[] = {
+ &dev_attr_msi_mode.attr,
+ NULL
+};
+
+ATTRIBUTE_GROUPS(gt683r_led);

static int gt683r_led_probe(struct hid_device *hdev,
const struct hid_device_id *id)
@@ -261,6 +270,8 @@ static int gt683r_led_probe(struct hid_device *hdev,
led->led_devs[i].name = name;
led->led_devs[i].max_brightness = 1;
led->led_devs[i].brightness_set = gt683r_brightness_set;
+ led->led_devs[i].groups = gt683r_led_groups;
+
ret = led_classdev_register(&hdev->dev, &led->led_devs[i]);
if (ret) {
hid_err(hdev, "could not register led device\n");
@@ -268,12 +279,6 @@ static int gt683r_led_probe(struct hid_device *hdev,
}
}

- ret = device_create_file(&led->hdev->dev, &dev_attr_leds_mode);
- if (ret) {
- hid_err(hdev, "could not make mode attribute file\n");
- goto fail;
- }
-
return 0;

fail:
@@ -288,7 +293,6 @@ static void gt683r_led_remove(struct hid_device *hdev)
int i;
struct gt683r_led *led = hid_get_drvdata(hdev);

- device_remove_file(&hdev->dev, &dev_attr_leds_mode);
for (i = 0; i < GT683R_LED_COUNT; i++)
led_classdev_unregister(&led->led_devs[i]);
flush_work(&led->work);
--
1.9.2

2014-06-25 19:09:38

by Jiri Kosina

[permalink] [raw]
Subject: Re: [PATCH 2/2 v2] HID: leds: Use attribute-groups in MSI GT683R driver

On Wed, 25 Jun 2014, Johan Hovold wrote:

> Did you see the attribute-race series I posted? Not sure how best to
> handle the dependency, as those patches should probably go in through
> the LEDs tree, while the first patch in that series (adding the groups
> field) is a dependency for this patch.
>
> Jiri, how would this best be solved?

I think the best course of action here is to gather Acks from the
respective maintainers, and take the whole lot trough a single tree
(probably the leds tree in this case) to avoid unnecessary intra-tree
dependencies in a rather straighforward situation like this.

Thanks,

--
Jiri Kosina
SUSE Labs

2014-06-25 22:55:36

by Bryan Wu

[permalink] [raw]
Subject: Re: [PATCH 2/2 v2] HID: leds: Use attribute-groups in MSI GT683R driver

On Wed, Jun 25, 2014 at 12:09 PM, Jiri Kosina <[email protected]> wrote:
> On Wed, 25 Jun 2014, Johan Hovold wrote:
>
>> Did you see the attribute-race series I posted? Not sure how best to
>> handle the dependency, as those patches should probably go in through
>> the LEDs tree, while the first patch in that series (adding the groups
>> field) is a dependency for this patch.
>>
>> Jiri, how would this best be solved?
>
> I think the best course of action here is to gather Acks from the
> respective maintainers, and take the whole lot trough a single tree
> (probably the leds tree in this case) to avoid unnecessary intra-tree
> dependencies in a rather straighforward situation like this.
>

I think the better place is HID/input tree, since this patch depends
on the initial one which is not in my tree.
I'm going to merge Johan's whole patchset and this patch probably
depends Johan's work too.

-Bryan

2014-06-30 10:40:37

by Johan Hovold

[permalink] [raw]
Subject: Re: [PATCH 2/2 v3] HID: leds: move led_mode attribute to led-class devices in MSI GT683R driver

On Wed, Jun 25, 2014 at 09:13:09PM +0300, Janne Kanniainen wrote:
> Move led_mode attribute from HID device to led-class devices and rename it
> msi_mode. This will also fix race condition by using attribute-groups.
>
> Signed-off-by: Janne Kanniainen <[email protected]>
> ---
>
> Changes in v3:
> - Style fixes
> - Rename sysfs-class-hid-driver-gt683r to sysfs-class-leds-driver-gt683r
>
> .../ABI/testing/sysfs-class-hid-driver-gt683r | 14 -----------
> .../ABI/testing/sysfs-class-leds-driver-gt683r | 16 +++++++++++++
> drivers/hid/hid-gt683r.c | 28 ++++++++++++----------
> 3 files changed, 32 insertions(+), 26 deletions(-)
> delete mode 100644 Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
> create mode 100644 Documentation/ABI/testing/sysfs-class-leds-driver-gt683r
>
> diff --git a/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
> deleted file mode 100644
> index 317e9d5..0000000
> --- a/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
> +++ /dev/null
> @@ -1,14 +0,0 @@
> -What: /sys/class/hidraw/<hidraw>/device/leds_mode
> -Date: Jun 2014
> -KernelVersion: 3.17
> -Contact: Janne Kanniainen <[email protected]>
> -Description:
> - Set the mode of LEDs
> -
> - 0 - normal
> - 1 - audio
> - 2 - breathing
> -
> - Normal: LEDs are fully on when enabled
> - Audio: LEDs brightness depends on sound level
> - Breathing: LEDs brightness varies at human breathing rate
> \ No newline at end of file
> diff --git a/Documentation/ABI/testing/sysfs-class-leds-driver-gt683r b/Documentation/ABI/testing/sysfs-class-leds-driver-gt683r
> new file mode 100644
> index 0000000..29769fb
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-class-leds-driver-gt683r
> @@ -0,0 +1,16 @@
> +What: /sys/class/leds/<led>/msi_mode

The ABI-file name now sort of matches the attribute path and there are
examples of attributes being documented in this particular way, but
naming is far from consistent in Documentation/ABI.

Perhaps we should use the name field of the attribute group and kill two
birds with one stone by making the sysfs file name match the attribute
path, while also making it even more obvious that the mode attribute is a
driver specific attribute (and not a common led class one) by placing it
in a subdirectory.

That is, if you set the .name field to "gt683r" (and rename the
attribute and ABI-file again) then the file name and attribute path
could match:

Documentation/ABI/testing/sysfs-class-leds-gt683r

What: /sys/class/leds/<led>/gt683r/mode

Both patches look good otherwise.

Johan

2014-06-30 10:48:24

by Johan Hovold

[permalink] [raw]
Subject: Re: [PATCH 2/2 v2] HID: leds: Use attribute-groups in MSI GT683R driver

On Wed, Jun 25, 2014 at 03:55:10PM -0700, Bryan Wu wrote:
> On Wed, Jun 25, 2014 at 12:09 PM, Jiri Kosina <[email protected]> wrote:
> > On Wed, 25 Jun 2014, Johan Hovold wrote:
> >
> >> Did you see the attribute-race series I posted? Not sure how best to
> >> handle the dependency, as those patches should probably go in through
> >> the LEDs tree, while the first patch in that series (adding the groups
> >> field) is a dependency for this patch.
> >>
> >> Jiri, how would this best be solved?
> >
> > I think the best course of action here is to gather Acks from the
> > respective maintainers, and take the whole lot trough a single tree
> > (probably the leds tree in this case) to avoid unnecessary intra-tree
> > dependencies in a rather straighforward situation like this.
>
> I think the better place is HID/input tree, since this patch depends
> on the initial one which is not in my tree.
> I'm going to merge Johan's whole patchset and this patch probably
> depends Johan's work too.

Dmitry has ACKed the input-patch and Bryan has applied that one and the
leds-patches to his tree (of which the first one is a dependency of this
patch).

Jiri, are you saying that the gt683r-driver should go in through his
tree as well, that is all three patches including the first that you
have already applied? I just assumed your for-next branch was immutable,
but perhaps I was mistaken.

Thanks,
Johan

2014-06-30 11:33:24

by Jiri Kosina

[permalink] [raw]
Subject: Re: [PATCH 2/2 v2] HID: leds: Use attribute-groups in MSI GT683R driver

On Mon, 30 Jun 2014, Johan Hovold wrote:

> > I think the better place is HID/input tree, since this patch depends
> > on the initial one which is not in my tree.
> > I'm going to merge Johan's whole patchset and this patch probably
> > depends Johan's work too.
>
> Dmitry has ACKed the input-patch and Bryan has applied that one and the
> leds-patches to his tree (of which the first one is a dependency of this
> patch).
>
> Jiri, are you saying that the gt683r-driver should go in through his
> tree as well, that is all three patches including the first that you
> have already applied? I just assumed your for-next branch was immutable,
> but perhaps I was mistaken.

Well, for-next branch is a collection of all the topic branches I am
queuing for the following merge window.

I am never really rebasing it, but I can definitely not include
'for-3.17/hid-gt683r' topic branch in the pile I will be sending to Linus
(all the scheduled branches are getting merged into 'for-linus' only when
merge window open). So the only potential conflict between hid.git and
Bryan's tree would be in linux-next (and probably there will be none, git
can handle duplicate patches nicely).

So once Bryan confirms he's queued it (please preserve my Signoff from my
tree), then I will just not include for-3.17/hid-gt683r branch in pull
request to Linus and all is fine.

Thanks,

--
Jiri Kosina
SUSE Labs

2014-06-30 23:17:35

by Bryan Wu

[permalink] [raw]
Subject: Re: [PATCH 2/2 v2] HID: leds: Use attribute-groups in MSI GT683R driver

On Mon, Jun 30, 2014 at 4:33 AM, Jiri Kosina <[email protected]> wrote:
> On Mon, 30 Jun 2014, Johan Hovold wrote:
>
>> > I think the better place is HID/input tree, since this patch depends
>> > on the initial one which is not in my tree.
>> > I'm going to merge Johan's whole patchset and this patch probably
>> > depends Johan's work too.
>>
>> Dmitry has ACKed the input-patch and Bryan has applied that one and the
>> leds-patches to his tree (of which the first one is a dependency of this
>> patch).
>>
>> Jiri, are you saying that the gt683r-driver should go in through his
>> tree as well, that is all three patches including the first that you
>> have already applied? I just assumed your for-next branch was immutable,
>> but perhaps I was mistaken.
>
> Well, for-next branch is a collection of all the topic branches I am
> queuing for the following merge window.
>
> I am never really rebasing it, but I can definitely not include
> 'for-3.17/hid-gt683r' topic branch in the pile I will be sending to Linus
> (all the scheduled branches are getting merged into 'for-linus' only when
> merge window open). So the only potential conflict between hid.git and
> Bryan's tree would be in linux-next (and probably there will be none, git
> can handle duplicate patches nicely).
>
> So once Bryan confirms he's queued it (please preserve my Signoff from my
> tree), then I will just not include for-3.17/hid-gt683r branch in pull
> request to Linus and all is fine.
>

I'm OK to merge Janne's first patch for HID GT683R through my tree
with you guys' SOB.
I'm also OK to merge this incremental patchset here. Please confirm it
if I didn't misunderstand here.

Also Janne or someone, can you post the original first patch to me or
point me where is it?

Thanks,
-Bryan

2014-07-01 08:49:17

by Johan Hovold

[permalink] [raw]
Subject: Re: [PATCH 2/2 v2] HID: leds: Use attribute-groups in MSI GT683R driver

On Mon, Jun 30, 2014 at 04:17:10PM -0700, Bryan Wu wrote:
> On Mon, Jun 30, 2014 at 4:33 AM, Jiri Kosina <[email protected]> wrote:
> > On Mon, 30 Jun 2014, Johan Hovold wrote:
> >
> >> > I think the better place is HID/input tree, since this patch depends
> >> > on the initial one which is not in my tree.
> >> > I'm going to merge Johan's whole patchset and this patch probably
> >> > depends Johan's work too.
> >>
> >> Dmitry has ACKed the input-patch and Bryan has applied that one and the
> >> leds-patches to his tree (of which the first one is a dependency of this
> >> patch).
> >>
> >> Jiri, are you saying that the gt683r-driver should go in through his
> >> tree as well, that is all three patches including the first that you
> >> have already applied? I just assumed your for-next branch was immutable,
> >> but perhaps I was mistaken.
> >
> > Well, for-next branch is a collection of all the topic branches I am
> > queuing for the following merge window.
> >
> > I am never really rebasing it, but I can definitely not include
> > 'for-3.17/hid-gt683r' topic branch in the pile I will be sending to Linus
> > (all the scheduled branches are getting merged into 'for-linus' only when
> > merge window open). So the only potential conflict between hid.git and
> > Bryan's tree would be in linux-next (and probably there will be none, git
> > can handle duplicate patches nicely).
> >
> > So once Bryan confirms he's queued it (please preserve my Signoff from my
> > tree), then I will just not include for-3.17/hid-gt683r branch in pull
> > request to Linus and all is fine.
> >
>
> I'm OK to merge Janne's first patch for HID GT683R through my tree
> with you guys' SOB.
> I'm also OK to merge this incremental patchset here. Please confirm it
> if I didn't misunderstand here.

That's correct. But the incremental patch set might need one more spin
before it is ready to be applied.

> Also Janne or someone, can you post the original first patch to me or
> point me where is it?

You could cherry-pick it from Jiri's for-3.17/hid-gt683r branch at

git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid.git

commit 4e3ed79670e0 ("HID: add support for MSI GT683R led panels").

Otherwise, the patch is here:

http://marc.info/?l=linux-usb&m=140310755911256&w=2

Thanks,
Johan

2014-07-01 15:48:53

by Bryan Wu

[permalink] [raw]
Subject: Re: [PATCH 2/2 v2] HID: leds: Use attribute-groups in MSI GT683R driver

On Tue, Jul 1, 2014 at 1:48 AM, Johan Hovold <[email protected]> wrote:
> On Mon, Jun 30, 2014 at 04:17:10PM -0700, Bryan Wu wrote:
>> On Mon, Jun 30, 2014 at 4:33 AM, Jiri Kosina <[email protected]> wrote:
>> > On Mon, 30 Jun 2014, Johan Hovold wrote:
>> >
>> >> > I think the better place is HID/input tree, since this patch depends
>> >> > on the initial one which is not in my tree.
>> >> > I'm going to merge Johan's whole patchset and this patch probably
>> >> > depends Johan's work too.
>> >>
>> >> Dmitry has ACKed the input-patch and Bryan has applied that one and the
>> >> leds-patches to his tree (of which the first one is a dependency of this
>> >> patch).
>> >>
>> >> Jiri, are you saying that the gt683r-driver should go in through his
>> >> tree as well, that is all three patches including the first that you
>> >> have already applied? I just assumed your for-next branch was immutable,
>> >> but perhaps I was mistaken.
>> >
>> > Well, for-next branch is a collection of all the topic branches I am
>> > queuing for the following merge window.
>> >
>> > I am never really rebasing it, but I can definitely not include
>> > 'for-3.17/hid-gt683r' topic branch in the pile I will be sending to Linus
>> > (all the scheduled branches are getting merged into 'for-linus' only when
>> > merge window open). So the only potential conflict between hid.git and
>> > Bryan's tree would be in linux-next (and probably there will be none, git
>> > can handle duplicate patches nicely).
>> >
>> > So once Bryan confirms he's queued it (please preserve my Signoff from my
>> > tree), then I will just not include for-3.17/hid-gt683r branch in pull
>> > request to Linus and all is fine.
>> >
>>
>> I'm OK to merge Janne's first patch for HID GT683R through my tree
>> with you guys' SOB.
>> I'm also OK to merge this incremental patchset here. Please confirm it
>> if I didn't misunderstand here.
>
> That's correct. But the incremental patch set might need one more spin
> before it is ready to be applied.
>
>> Also Janne or someone, can you post the original first patch to me or
>> point me where is it?
>
> You could cherry-pick it from Jiri's for-3.17/hid-gt683r branch at
>
> git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid.git
>
> commit 4e3ed79670e0 ("HID: add support for MSI GT683R led panels").
>
> Otherwise, the patch is here:
>
> http://marc.info/?l=linux-usb&m=140310755911256&w=2
>

Great, I just cherry-picked and applied to my tree
http://git.kernel.org/cgit/linux/kernel/git/cooloney/linux-leds.git/commit/?h=for-next&id=f471d9480275796dea2ac7ec249b050e70a2888d

Thanks,
-Bryan

2014-07-01 17:51:36

by Janne Kanniainen

[permalink] [raw]
Subject: [PATCH 2/2 v4] HID: leds: move led_mode attribute to led-class devices in MSI GT683R driver

Move led_mode attribute from HID device to led-class devices and rename it
msi_mode. This will also fix race condition by using attribute-groups.

Signed-off-by: Janne Kanniainen <[email protected]>
---

Changes in v3:
- Style fixes
- Rename sysfs-class-hid-driver-gt683r to sysfs-class-leds-driver-gt683r

Changes in v4:
- Rename sysfs-class-leds-driver-gt683r to sysfs-class-leds-gt683r
- Change "What: " to /sys/class/leds/<led>/gt683r/mode
- Change .name from gt683r_led to gt683r

.../ABI/testing/sysfs-class-hid-driver-gt683r | 14 ----------
Documentation/ABI/testing/sysfs-class-leds-gt683r | 16 ++++++++++++
drivers/hid/hid-gt683r.c | 30 ++++++++++++----------
3 files changed, 33 insertions(+), 27 deletions(-)
delete mode 100644 Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
create mode 100644 Documentation/ABI/testing/sysfs-class-leds-gt683r

diff --git a/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
deleted file mode 100644
index 317e9d5..0000000
--- a/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
+++ /dev/null
@@ -1,14 +0,0 @@
-What: /sys/class/hidraw/<hidraw>/device/leds_mode
-Date: Jun 2014
-KernelVersion: 3.17
-Contact: Janne Kanniainen <[email protected]>
-Description:
- Set the mode of LEDs
-
- 0 - normal
- 1 - audio
- 2 - breathing
-
- Normal: LEDs are fully on when enabled
- Audio: LEDs brightness depends on sound level
- Breathing: LEDs brightness varies at human breathing rate
\ No newline at end of file
diff --git a/Documentation/ABI/testing/sysfs-class-leds-gt683r b/Documentation/ABI/testing/sysfs-class-leds-gt683r
new file mode 100644
index 0000000..e4fae60
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-leds-gt683r
@@ -0,0 +1,16 @@
+What: /sys/class/leds/<led>/gt683r/mode
+Date: Jun 2014
+KernelVersion: 3.17
+Contact: Janne Kanniainen <[email protected]>
+Description:
+ Set the mode of LEDs. You should notice that changing the mode
+ of one LED will update the mode of its two sibling devices as
+ well.
+
+ 0 - normal
+ 1 - audio
+ 2 - breathing
+
+ Normal: LEDs are fully on when enabled
+ Audio: LEDs brightness depends on sound level
+ Breathing: LEDs brightness varies at human breathing rate
\ No newline at end of file
diff --git a/drivers/hid/hid-gt683r.c b/drivers/hid/hid-gt683r.c
index 073bd80..f743444 100644
--- a/drivers/hid/hid-gt683r.c
+++ b/drivers/hid/hid-gt683r.c
@@ -84,12 +84,13 @@ static void gt683r_brightness_set(struct led_classdev *led_cdev,
}
}

-static ssize_t leds_mode_show(struct device *dev,
+static ssize_t mode_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
u8 sysfs_mode;
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = container_of(dev->parent,
+ struct hid_device, dev);
struct gt683r_led *led = hid_get_drvdata(hdev);

if (led->mode == GT683R_LED_NORMAL)
@@ -102,12 +103,13 @@ static ssize_t leds_mode_show(struct device *dev,
return scnprintf(buf, PAGE_SIZE, "%u\n", sysfs_mode);
}

-static ssize_t leds_mode_store(struct device *dev,
+static ssize_t mode_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
u8 sysfs_mode;
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = container_of(dev->parent,
+ struct hid_device, dev);
struct gt683r_led *led = hid_get_drvdata(hdev);


@@ -212,7 +214,14 @@ fail:
mutex_unlock(&led->lock);
}

-static DEVICE_ATTR_RW(leds_mode);
+static DEVICE_ATTR_RW(mode);
+
+static struct attribute *gt683r_led_attrs[] = {
+ &dev_attr_mode.attr,
+ NULL
+};
+
+ATTRIBUTE_GROUPS(gt683r_led);

static int gt683r_led_probe(struct hid_device *hdev,
const struct hid_device_id *id)
@@ -261,6 +270,8 @@ static int gt683r_led_probe(struct hid_device *hdev,
led->led_devs[i].name = name;
led->led_devs[i].max_brightness = 1;
led->led_devs[i].brightness_set = gt683r_brightness_set;
+ led->led_devs[i].groups = gt683r_led_groups;
+
ret = led_classdev_register(&hdev->dev, &led->led_devs[i]);
if (ret) {
hid_err(hdev, "could not register led device\n");
@@ -268,12 +279,6 @@ static int gt683r_led_probe(struct hid_device *hdev,
}
}

- ret = device_create_file(&led->hdev->dev, &dev_attr_leds_mode);
- if (ret) {
- hid_err(hdev, "could not make mode attribute file\n");
- goto fail;
- }
-
return 0;

fail:
@@ -288,7 +293,6 @@ static void gt683r_led_remove(struct hid_device *hdev)
int i;
struct gt683r_led *led = hid_get_drvdata(hdev);

- device_remove_file(&hdev->dev, &dev_attr_leds_mode);
for (i = 0; i < GT683R_LED_COUNT; i++)
led_classdev_unregister(&led->led_devs[i]);
flush_work(&led->work);
@@ -298,7 +302,7 @@ static void gt683r_led_remove(struct hid_device *hdev)
static struct hid_driver gt683r_led_driver = {
.probe = gt683r_led_probe,
.remove = gt683r_led_remove,
- .name = "gt683r_led",
+ .name = "gt683r",
.id_table = gt683r_led_id,
};

--
1.9.2

Did you mean like this?

2014-07-01 20:17:06

by Johan Hovold

[permalink] [raw]
Subject: Re: [PATCH 2/2 v4] HID: leds: move led_mode attribute to led-class devices in MSI GT683R driver

On Tue, Jul 01, 2014 at 08:50:54PM +0300, Janne Kanniainen wrote:

> -static DEVICE_ATTR_RW(leds_mode);
> +static DEVICE_ATTR_RW(mode);
> +
> +static struct attribute *gt683r_led_attrs[] = {
> + &dev_attr_mode.attr,
> + NULL
> +};
> +
> +ATTRIBUTE_GROUPS(gt683r_led);

I was referring to the name field of struct attribute_group (and not of
struct hid_driver). Specifically, that means that you cannot use the
ATTRIBUTE_GROUPS macro (which leaves the name field unset), but rather
should define the two corresponding structs directly as follows:

static const struct attribute_group gt683r_led_group = {
.name = "gt683r",
.attrs = gt683r_led_attrs,
};

static const struct attribute_group *gt683r_led_groups[] = {
&gt683r_led_group,
NULL
};

That way the mode attribute will be created in a subdirectory (named
gt683r) of the led class device.

> static int gt683r_led_probe(struct hid_device *hdev,
> const struct hid_device_id *id)
> @@ -261,6 +270,8 @@ static int gt683r_led_probe(struct hid_device *hdev,
> led->led_devs[i].name = name;
> led->led_devs[i].max_brightness = 1;
> led->led_devs[i].brightness_set = gt683r_brightness_set;
> + led->led_devs[i].groups = gt683r_led_groups;
> +
> ret = led_classdev_register(&hdev->dev, &led->led_devs[i]);
> if (ret) {
> hid_err(hdev, "could not register led device\n");
> @@ -268,12 +279,6 @@ static int gt683r_led_probe(struct hid_device *hdev,
> }
> }

<snip>

> @@ -298,7 +302,7 @@ static void gt683r_led_remove(struct hid_device *hdev)
> static struct hid_driver gt683r_led_driver = {
> .probe = gt683r_led_probe,
> .remove = gt683r_led_remove,
> - .name = "gt683r_led",
> + .name = "gt683r",

So you should skip this bit.

> .id_table = gt683r_led_id,
> };

Johan

2014-07-02 08:56:52

by Jiri Kosina

[permalink] [raw]
Subject: Re: [PATCH 2/2 v2] HID: leds: Use attribute-groups in MSI GT683R driver

On Tue, 1 Jul 2014, Bryan Wu wrote:

> Great, I just cherry-picked and applied to my tree
> http://git.kernel.org/cgit/linux/kernel/git/cooloney/linux-leds.git/commit/?h=for-next&id=f471d9480275796dea2ac7ec249b050e70a2888d

Ok, perfect, thanks. I am marking my 'for-3.17/hid-gt683r' so that it
wouldn't be contained in the for-linus merge window pile.

--
Jiri Kosina
SUSE Labs

2014-07-02 17:38:18

by Janne Kanniainen

[permalink] [raw]
Subject: [PATCH 2/2 v5] HID: leds: move led_mode attribute to led-class devices in MSI GT683R driver

Move led_mode attribute from HID device to led-class devices. This will also fix race condition by using attribute-groups.

Signed-off-by: Janne Kanniainen <[email protected]>
---

Changes in v3:
- Style fixes
- Rename sysfs-class-hid-driver-gt683r to sysfs-class-leds-driver-gt683r

Changes in v4:
- Rename sysfs-class-leds-driver-gt683r to sysfs-class-leds-gt683r
- Change "What: " to /sys/class/leds/<led>/gt683r/mode
- Change .name from gt683r_led to gt683r

Changes in v5:
- Move mode attribute to gt683r/mode

.../ABI/testing/sysfs-class-hid-driver-gt683r | 14 ---------
Documentation/ABI/testing/sysfs-class-leds-gt683r | 16 ++++++++++
drivers/hid/hid-gt683r.c | 36 ++++++++++++++--------
3 files changed, 40 insertions(+), 26 deletions(-)
delete mode 100644 Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
create mode 100644 Documentation/ABI/testing/sysfs-class-leds-gt683r

diff --git a/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
deleted file mode 100644
index 317e9d5..0000000
--- a/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
+++ /dev/null
@@ -1,14 +0,0 @@
-What: /sys/class/hidraw/<hidraw>/device/leds_mode
-Date: Jun 2014
-KernelVersion: 3.17
-Contact: Janne Kanniainen <[email protected]>
-Description:
- Set the mode of LEDs
-
- 0 - normal
- 1 - audio
- 2 - breathing
-
- Normal: LEDs are fully on when enabled
- Audio: LEDs brightness depends on sound level
- Breathing: LEDs brightness varies at human breathing rate
\ No newline at end of file
diff --git a/Documentation/ABI/testing/sysfs-class-leds-gt683r b/Documentation/ABI/testing/sysfs-class-leds-gt683r
new file mode 100644
index 0000000..e4fae60
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-leds-gt683r
@@ -0,0 +1,16 @@
+What: /sys/class/leds/<led>/gt683r/mode
+Date: Jun 2014
+KernelVersion: 3.17
+Contact: Janne Kanniainen <[email protected]>
+Description:
+ Set the mode of LEDs. You should notice that changing the mode
+ of one LED will update the mode of its two sibling devices as
+ well.
+
+ 0 - normal
+ 1 - audio
+ 2 - breathing
+
+ Normal: LEDs are fully on when enabled
+ Audio: LEDs brightness depends on sound level
+ Breathing: LEDs brightness varies at human breathing rate
\ No newline at end of file
diff --git a/drivers/hid/hid-gt683r.c b/drivers/hid/hid-gt683r.c
index 073bd80..0d6f135 100644
--- a/drivers/hid/hid-gt683r.c
+++ b/drivers/hid/hid-gt683r.c
@@ -84,12 +84,13 @@ static void gt683r_brightness_set(struct led_classdev *led_cdev,
}
}

-static ssize_t leds_mode_show(struct device *dev,
+static ssize_t mode_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
u8 sysfs_mode;
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = container_of(dev->parent,
+ struct hid_device, dev);
struct gt683r_led *led = hid_get_drvdata(hdev);

if (led->mode == GT683R_LED_NORMAL)
@@ -102,12 +103,13 @@ static ssize_t leds_mode_show(struct device *dev,
return scnprintf(buf, PAGE_SIZE, "%u\n", sysfs_mode);
}

-static ssize_t leds_mode_store(struct device *dev,
+static ssize_t mode_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
u8 sysfs_mode;
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = container_of(dev->parent,
+ struct hid_device, dev);
struct gt683r_led *led = hid_get_drvdata(hdev);


@@ -212,7 +214,22 @@ fail:
mutex_unlock(&led->lock);
}

-static DEVICE_ATTR_RW(leds_mode);
+static DEVICE_ATTR_RW(mode);
+
+static struct attribute *gt683r_led_attrs[] = {
+ &dev_attr_mode.attr,
+ NULL
+};
+
+static const struct attribute_group gt683r_led_group = {
+ .name = "gt683r",
+ .attrs = gt683r_led_attrs,
+};
+
+static const struct attribute_group *gt683r_led_groups[] = {
+ &gt683r_led_group,
+ NULL
+};

static int gt683r_led_probe(struct hid_device *hdev,
const struct hid_device_id *id)
@@ -261,6 +278,8 @@ static int gt683r_led_probe(struct hid_device *hdev,
led->led_devs[i].name = name;
led->led_devs[i].max_brightness = 1;
led->led_devs[i].brightness_set = gt683r_brightness_set;
+ led->led_devs[i].groups = gt683r_led_groups;
+
ret = led_classdev_register(&hdev->dev, &led->led_devs[i]);
if (ret) {
hid_err(hdev, "could not register led device\n");
@@ -268,12 +287,6 @@ static int gt683r_led_probe(struct hid_device *hdev,
}
}

- ret = device_create_file(&led->hdev->dev, &dev_attr_leds_mode);
- if (ret) {
- hid_err(hdev, "could not make mode attribute file\n");
- goto fail;
- }
-
return 0;

fail:
@@ -288,7 +301,6 @@ static void gt683r_led_remove(struct hid_device *hdev)
int i;
struct gt683r_led *led = hid_get_drvdata(hdev);

- device_remove_file(&hdev->dev, &dev_attr_leds_mode);
for (i = 0; i < GT683R_LED_COUNT; i++)
led_classdev_unregister(&led->led_devs[i]);
flush_work(&led->work);
--
1.9.2

2014-07-03 08:29:14

by Johan Hovold

[permalink] [raw]
Subject: Re: [PATCH 2/2 v5] HID: leds: move led_mode attribute to led-class devices in MSI GT683R driver

On Wed, Jul 02, 2014 at 08:37:59PM +0300, Janne Kanniainen wrote:
> Move led_mode attribute from HID device to led-class devices. This will also fix race condition by using attribute-groups.
>
> Signed-off-by: Janne Kanniainen <[email protected]>

Looks good now. Thanks!

Perhaps you can resend both patches as a series (call both v6) so it's
easy to figure which version that is to be applied.

I also noticed that your subject line is a bit long (will end up as the
commit summary). Using for example

HID: gt683r: move mode attribute to led-class devices

would make it more concise.

Oh, and your commit message lacks line breaks (around 72 cols). You
could fix that as well.

Thanks again,
Johan

2014-07-03 17:17:20

by Janne Kanniainen

[permalink] [raw]
Subject: [PATCH 1/2 v6] HID: gt683r: fix race condition

This will fix race condition noticed by Oliver Neukum. Sysfs files are
created before mutex and work are initialized.

Signed-off-by: Janne Kanniainen <[email protected]>
---
drivers/hid/hid-gt683r.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/hid/hid-gt683r.c b/drivers/hid/hid-gt683r.c
index 077f7a1..073bd80 100644
--- a/drivers/hid/hid-gt683r.c
+++ b/drivers/hid/hid-gt683r.c
@@ -227,6 +227,9 @@ static int gt683r_led_probe(struct hid_device *hdev,
if (!led)
return -ENOMEM;

+ mutex_init(&led->lock);
+ INIT_WORK(&led->work, gt683r_led_work);
+
led->mode = GT683R_LED_NORMAL;
led->hdev = hdev;
hid_set_drvdata(hdev, led);
@@ -271,9 +274,6 @@ static int gt683r_led_probe(struct hid_device *hdev,
goto fail;
}

- mutex_init(&led->lock);
- INIT_WORK(&led->work, gt683r_led_work);
-
return 0;

fail:
--
1.9.2

2014-07-03 17:17:34

by Janne Kanniainen

[permalink] [raw]
Subject: [PATCH 2/2 v6] HID: gt683r: move mode attribute to led-class devices

Move led_mode attribute from HID device to led-class devices and rename
it msi_mode. This will also fix race condition by using
attribute-groups.

Signed-off-by: Janne Kanniainen <[email protected]>
---

Changes in v3:
- Style fixes
- Rename sysfs-class-hid-driver-gt683r to sysfs-class-leds-driver-gt683r

Changes in v4:
- Rename sysfs-class-leds-driver-gt683r to sysfs-class-leds-gt683r
- Change "What: " to /sys/class/leds/<led>/gt683r/mode
- Change .name from gt683r_led to gt683r

Changes in v5:
- Move mode attribute to gt683r/mode

Changes in v6:
- Fix subject and commit message

.../ABI/testing/sysfs-class-hid-driver-gt683r | 14 ---------
Documentation/ABI/testing/sysfs-class-leds-gt683r | 16 ++++++++++
drivers/hid/hid-gt683r.c | 36 ++++++++++++++--------
3 files changed, 40 insertions(+), 26 deletions(-)
delete mode 100644 Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
create mode 100644 Documentation/ABI/testing/sysfs-class-leds-gt683r

diff --git a/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
deleted file mode 100644
index 317e9d5..0000000
--- a/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
+++ /dev/null
@@ -1,14 +0,0 @@
-What: /sys/class/hidraw/<hidraw>/device/leds_mode
-Date: Jun 2014
-KernelVersion: 3.17
-Contact: Janne Kanniainen <[email protected]>
-Description:
- Set the mode of LEDs
-
- 0 - normal
- 1 - audio
- 2 - breathing
-
- Normal: LEDs are fully on when enabled
- Audio: LEDs brightness depends on sound level
- Breathing: LEDs brightness varies at human breathing rate
\ No newline at end of file
diff --git a/Documentation/ABI/testing/sysfs-class-leds-gt683r b/Documentation/ABI/testing/sysfs-class-leds-gt683r
new file mode 100644
index 0000000..e4fae60
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-leds-gt683r
@@ -0,0 +1,16 @@
+What: /sys/class/leds/<led>/gt683r/mode
+Date: Jun 2014
+KernelVersion: 3.17
+Contact: Janne Kanniainen <[email protected]>
+Description:
+ Set the mode of LEDs. You should notice that changing the mode
+ of one LED will update the mode of its two sibling devices as
+ well.
+
+ 0 - normal
+ 1 - audio
+ 2 - breathing
+
+ Normal: LEDs are fully on when enabled
+ Audio: LEDs brightness depends on sound level
+ Breathing: LEDs brightness varies at human breathing rate
\ No newline at end of file
diff --git a/drivers/hid/hid-gt683r.c b/drivers/hid/hid-gt683r.c
index 073bd80..0d6f135 100644
--- a/drivers/hid/hid-gt683r.c
+++ b/drivers/hid/hid-gt683r.c
@@ -84,12 +84,13 @@ static void gt683r_brightness_set(struct led_classdev *led_cdev,
}
}

-static ssize_t leds_mode_show(struct device *dev,
+static ssize_t mode_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
u8 sysfs_mode;
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = container_of(dev->parent,
+ struct hid_device, dev);
struct gt683r_led *led = hid_get_drvdata(hdev);

if (led->mode == GT683R_LED_NORMAL)
@@ -102,12 +103,13 @@ static ssize_t leds_mode_show(struct device *dev,
return scnprintf(buf, PAGE_SIZE, "%u\n", sysfs_mode);
}

-static ssize_t leds_mode_store(struct device *dev,
+static ssize_t mode_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
u8 sysfs_mode;
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = container_of(dev->parent,
+ struct hid_device, dev);
struct gt683r_led *led = hid_get_drvdata(hdev);


@@ -212,7 +214,22 @@ fail:
mutex_unlock(&led->lock);
}

-static DEVICE_ATTR_RW(leds_mode);
+static DEVICE_ATTR_RW(mode);
+
+static struct attribute *gt683r_led_attrs[] = {
+ &dev_attr_mode.attr,
+ NULL
+};
+
+static const struct attribute_group gt683r_led_group = {
+ .name = "gt683r",
+ .attrs = gt683r_led_attrs,
+};
+
+static const struct attribute_group *gt683r_led_groups[] = {
+ &gt683r_led_group,
+ NULL
+};

static int gt683r_led_probe(struct hid_device *hdev,
const struct hid_device_id *id)
@@ -261,6 +278,8 @@ static int gt683r_led_probe(struct hid_device *hdev,
led->led_devs[i].name = name;
led->led_devs[i].max_brightness = 1;
led->led_devs[i].brightness_set = gt683r_brightness_set;
+ led->led_devs[i].groups = gt683r_led_groups;
+
ret = led_classdev_register(&hdev->dev, &led->led_devs[i]);
if (ret) {
hid_err(hdev, "could not register led device\n");
@@ -268,12 +287,6 @@ static int gt683r_led_probe(struct hid_device *hdev,
}
}

- ret = device_create_file(&led->hdev->dev, &dev_attr_leds_mode);
- if (ret) {
- hid_err(hdev, "could not make mode attribute file\n");
- goto fail;
- }
-
return 0;

fail:
@@ -288,7 +301,6 @@ static void gt683r_led_remove(struct hid_device *hdev)
int i;
struct gt683r_led *led = hid_get_drvdata(hdev);

- device_remove_file(&hdev->dev, &dev_attr_leds_mode);
for (i = 0; i < GT683R_LED_COUNT; i++)
led_classdev_unregister(&led->led_devs[i]);
flush_work(&led->work);
--
1.9.2

2014-07-03 17:35:01

by Johan Hovold

[permalink] [raw]
Subject: Re: [PATCH 1/2 v6] HID: gt683r: fix race condition

On Thu, Jul 03, 2014 at 08:17:08PM +0300, Janne Kanniainen wrote:
> This will fix race condition noticed by Oliver Neukum. Sysfs files are
> created before mutex and work are initialized.
>
> Signed-off-by: Janne Kanniainen <[email protected]>

Reviewed-by: Johan Hovold <[email protected]>

> ---
> drivers/hid/hid-gt683r.c | 6 +++---
> 1 file changed, 3 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/hid/hid-gt683r.c b/drivers/hid/hid-gt683r.c
> index 077f7a1..073bd80 100644
> --- a/drivers/hid/hid-gt683r.c
> +++ b/drivers/hid/hid-gt683r.c
> @@ -227,6 +227,9 @@ static int gt683r_led_probe(struct hid_device *hdev,
> if (!led)
> return -ENOMEM;
>
> + mutex_init(&led->lock);
> + INIT_WORK(&led->work, gt683r_led_work);
> +
> led->mode = GT683R_LED_NORMAL;
> led->hdev = hdev;
> hid_set_drvdata(hdev, led);
> @@ -271,9 +274,6 @@ static int gt683r_led_probe(struct hid_device *hdev,
> goto fail;
> }
>
> - mutex_init(&led->lock);
> - INIT_WORK(&led->work, gt683r_led_work);
> -
> return 0;
>
> fail:

2014-07-03 17:40:56

by Johan Hovold

[permalink] [raw]
Subject: Re: [PATCH 2/2 v6] HID: gt683r: move mode attribute to led-class devices

On Thu, Jul 03, 2014 at 08:17:09PM +0300, Janne Kanniainen wrote:
> Move led_mode attribute from HID device to led-class devices and rename
> it msi_mode. This will also fix race condition by using

There's a typo here (s/msi_mode/mode) but perhaps Bryan can just fix
that up before applying?

> attribute-groups.
>
> Signed-off-by: Janne Kanniainen <[email protected]>

Reviewed-by: Johan Hovold <[email protected]>

Otherwise both patches (v6) are ready to be merged, Bryan.

Thanks, Janne!

Johan

> ---
>
> Changes in v3:
> - Style fixes
> - Rename sysfs-class-hid-driver-gt683r to sysfs-class-leds-driver-gt683r
>
> Changes in v4:
> - Rename sysfs-class-leds-driver-gt683r to sysfs-class-leds-gt683r
> - Change "What: " to /sys/class/leds/<led>/gt683r/mode
> - Change .name from gt683r_led to gt683r
>
> Changes in v5:
> - Move mode attribute to gt683r/mode
>
> Changes in v6:
> - Fix subject and commit message
>
> .../ABI/testing/sysfs-class-hid-driver-gt683r | 14 ---------
> Documentation/ABI/testing/sysfs-class-leds-gt683r | 16 ++++++++++
> drivers/hid/hid-gt683r.c | 36 ++++++++++++++--------
> 3 files changed, 40 insertions(+), 26 deletions(-)
> delete mode 100644 Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
> create mode 100644 Documentation/ABI/testing/sysfs-class-leds-gt683r
>
> diff --git a/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
> deleted file mode 100644
> index 317e9d5..0000000
> --- a/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
> +++ /dev/null
> @@ -1,14 +0,0 @@
> -What: /sys/class/hidraw/<hidraw>/device/leds_mode
> -Date: Jun 2014
> -KernelVersion: 3.17
> -Contact: Janne Kanniainen <[email protected]>
> -Description:
> - Set the mode of LEDs
> -
> - 0 - normal
> - 1 - audio
> - 2 - breathing
> -
> - Normal: LEDs are fully on when enabled
> - Audio: LEDs brightness depends on sound level
> - Breathing: LEDs brightness varies at human breathing rate
> \ No newline at end of file
> diff --git a/Documentation/ABI/testing/sysfs-class-leds-gt683r b/Documentation/ABI/testing/sysfs-class-leds-gt683r
> new file mode 100644
> index 0000000..e4fae60
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-class-leds-gt683r
> @@ -0,0 +1,16 @@
> +What: /sys/class/leds/<led>/gt683r/mode
> +Date: Jun 2014
> +KernelVersion: 3.17
> +Contact: Janne Kanniainen <[email protected]>
> +Description:
> + Set the mode of LEDs. You should notice that changing the mode
> + of one LED will update the mode of its two sibling devices as
> + well.
> +
> + 0 - normal
> + 1 - audio
> + 2 - breathing
> +
> + Normal: LEDs are fully on when enabled
> + Audio: LEDs brightness depends on sound level
> + Breathing: LEDs brightness varies at human breathing rate
> \ No newline at end of file
> diff --git a/drivers/hid/hid-gt683r.c b/drivers/hid/hid-gt683r.c
> index 073bd80..0d6f135 100644
> --- a/drivers/hid/hid-gt683r.c
> +++ b/drivers/hid/hid-gt683r.c
> @@ -84,12 +84,13 @@ static void gt683r_brightness_set(struct led_classdev *led_cdev,
> }
> }
>
> -static ssize_t leds_mode_show(struct device *dev,
> +static ssize_t mode_show(struct device *dev,
> struct device_attribute *attr,
> char *buf)
> {
> u8 sysfs_mode;
> - struct hid_device *hdev = container_of(dev, struct hid_device, dev);
> + struct hid_device *hdev = container_of(dev->parent,
> + struct hid_device, dev);
> struct gt683r_led *led = hid_get_drvdata(hdev);
>
> if (led->mode == GT683R_LED_NORMAL)
> @@ -102,12 +103,13 @@ static ssize_t leds_mode_show(struct device *dev,
> return scnprintf(buf, PAGE_SIZE, "%u\n", sysfs_mode);
> }
>
> -static ssize_t leds_mode_store(struct device *dev,
> +static ssize_t mode_store(struct device *dev,
> struct device_attribute *attr,
> const char *buf, size_t count)
> {
> u8 sysfs_mode;
> - struct hid_device *hdev = container_of(dev, struct hid_device, dev);
> + struct hid_device *hdev = container_of(dev->parent,
> + struct hid_device, dev);
> struct gt683r_led *led = hid_get_drvdata(hdev);
>
>
> @@ -212,7 +214,22 @@ fail:
> mutex_unlock(&led->lock);
> }
>
> -static DEVICE_ATTR_RW(leds_mode);
> +static DEVICE_ATTR_RW(mode);
> +
> +static struct attribute *gt683r_led_attrs[] = {
> + &dev_attr_mode.attr,
> + NULL
> +};
> +
> +static const struct attribute_group gt683r_led_group = {
> + .name = "gt683r",
> + .attrs = gt683r_led_attrs,
> +};
> +
> +static const struct attribute_group *gt683r_led_groups[] = {
> + &gt683r_led_group,
> + NULL
> +};
>
> static int gt683r_led_probe(struct hid_device *hdev,
> const struct hid_device_id *id)
> @@ -261,6 +278,8 @@ static int gt683r_led_probe(struct hid_device *hdev,
> led->led_devs[i].name = name;
> led->led_devs[i].max_brightness = 1;
> led->led_devs[i].brightness_set = gt683r_brightness_set;
> + led->led_devs[i].groups = gt683r_led_groups;
> +
> ret = led_classdev_register(&hdev->dev, &led->led_devs[i]);
> if (ret) {
> hid_err(hdev, "could not register led device\n");
> @@ -268,12 +287,6 @@ static int gt683r_led_probe(struct hid_device *hdev,
> }
> }
>
> - ret = device_create_file(&led->hdev->dev, &dev_attr_leds_mode);
> - if (ret) {
> - hid_err(hdev, "could not make mode attribute file\n");
> - goto fail;
> - }
> -
> return 0;
>
> fail:
> @@ -288,7 +301,6 @@ static void gt683r_led_remove(struct hid_device *hdev)
> int i;
> struct gt683r_led *led = hid_get_drvdata(hdev);
>
> - device_remove_file(&hdev->dev, &dev_attr_leds_mode);
> for (i = 0; i < GT683R_LED_COUNT; i++)
> led_classdev_unregister(&led->led_devs[i]);
> flush_work(&led->work);

2014-07-03 18:13:34

by Bryan Wu

[permalink] [raw]
Subject: Re: [PATCH 1/2 v6] HID: gt683r: fix race condition

On Thu, Jul 3, 2014 at 10:34 AM, Johan Hovold <[email protected]> wrote:
> On Thu, Jul 03, 2014 at 08:17:08PM +0300, Janne Kanniainen wrote:
>> This will fix race condition noticed by Oliver Neukum. Sysfs files are
>> created before mutex and work are initialized.
>>
>> Signed-off-by: Janne Kanniainen <[email protected]>
>
> Reviewed-by: Johan Hovold <[email protected]>
>

Good, merged to my tree.
-Bryan

>> ---
>> drivers/hid/hid-gt683r.c | 6 +++---
>> 1 file changed, 3 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/hid/hid-gt683r.c b/drivers/hid/hid-gt683r.c
>> index 077f7a1..073bd80 100644
>> --- a/drivers/hid/hid-gt683r.c
>> +++ b/drivers/hid/hid-gt683r.c
>> @@ -227,6 +227,9 @@ static int gt683r_led_probe(struct hid_device *hdev,
>> if (!led)
>> return -ENOMEM;
>>
>> + mutex_init(&led->lock);
>> + INIT_WORK(&led->work, gt683r_led_work);
>> +
>> led->mode = GT683R_LED_NORMAL;
>> led->hdev = hdev;
>> hid_set_drvdata(hdev, led);
>> @@ -271,9 +274,6 @@ static int gt683r_led_probe(struct hid_device *hdev,
>> goto fail;
>> }
>>
>> - mutex_init(&led->lock);
>> - INIT_WORK(&led->work, gt683r_led_work);
>> -
>> return 0;
>>
>> fail:

2014-07-03 18:18:22

by Bryan Wu

[permalink] [raw]
Subject: Re: [PATCH 2/2 v6] HID: gt683r: move mode attribute to led-class devices

On Thu, Jul 3, 2014 at 10:40 AM, Johan Hovold <[email protected]> wrote:
> On Thu, Jul 03, 2014 at 08:17:09PM +0300, Janne Kanniainen wrote:
>> Move led_mode attribute from HID device to led-class devices and rename
>> it msi_mode. This will also fix race condition by using
>
> There's a typo here (s/msi_mode/mode) but perhaps Bryan can just fix
> that up before applying?
>
>> attribute-groups.
>>
>> Signed-off-by: Janne Kanniainen <[email protected]>
>
> Reviewed-by: Johan Hovold <[email protected]>
>
> Otherwise both patches (v6) are ready to be merged, Bryan.
>
> Thanks, Janne!
>

No problem. I fixed the typo and merged it.

Thanks,
-Bryan



>> ---
>>
>> Changes in v3:
>> - Style fixes
>> - Rename sysfs-class-hid-driver-gt683r to sysfs-class-leds-driver-gt683r
>>
>> Changes in v4:
>> - Rename sysfs-class-leds-driver-gt683r to sysfs-class-leds-gt683r
>> - Change "What: " to /sys/class/leds/<led>/gt683r/mode
>> - Change .name from gt683r_led to gt683r
>>
>> Changes in v5:
>> - Move mode attribute to gt683r/mode
>>
>> Changes in v6:
>> - Fix subject and commit message
>>
>> .../ABI/testing/sysfs-class-hid-driver-gt683r | 14 ---------
>> Documentation/ABI/testing/sysfs-class-leds-gt683r | 16 ++++++++++
>> drivers/hid/hid-gt683r.c | 36 ++++++++++++++--------
>> 3 files changed, 40 insertions(+), 26 deletions(-)
>> delete mode 100644 Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
>> create mode 100644 Documentation/ABI/testing/sysfs-class-leds-gt683r
>>
>> diff --git a/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
>> deleted file mode 100644
>> index 317e9d5..0000000
>> --- a/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
>> +++ /dev/null
>> @@ -1,14 +0,0 @@
>> -What: /sys/class/hidraw/<hidraw>/device/leds_mode
>> -Date: Jun 2014
>> -KernelVersion: 3.17
>> -Contact: Janne Kanniainen <[email protected]>
>> -Description:
>> - Set the mode of LEDs
>> -
>> - 0 - normal
>> - 1 - audio
>> - 2 - breathing
>> -
>> - Normal: LEDs are fully on when enabled
>> - Audio: LEDs brightness depends on sound level
>> - Breathing: LEDs brightness varies at human breathing rate
>> \ No newline at end of file
>> diff --git a/Documentation/ABI/testing/sysfs-class-leds-gt683r b/Documentation/ABI/testing/sysfs-class-leds-gt683r
>> new file mode 100644
>> index 0000000..e4fae60
>> --- /dev/null
>> +++ b/Documentation/ABI/testing/sysfs-class-leds-gt683r
>> @@ -0,0 +1,16 @@
>> +What: /sys/class/leds/<led>/gt683r/mode
>> +Date: Jun 2014
>> +KernelVersion: 3.17
>> +Contact: Janne Kanniainen <[email protected]>
>> +Description:
>> + Set the mode of LEDs. You should notice that changing the mode
>> + of one LED will update the mode of its two sibling devices as
>> + well.
>> +
>> + 0 - normal
>> + 1 - audio
>> + 2 - breathing
>> +
>> + Normal: LEDs are fully on when enabled
>> + Audio: LEDs brightness depends on sound level
>> + Breathing: LEDs brightness varies at human breathing rate
>> \ No newline at end of file
>> diff --git a/drivers/hid/hid-gt683r.c b/drivers/hid/hid-gt683r.c
>> index 073bd80..0d6f135 100644
>> --- a/drivers/hid/hid-gt683r.c
>> +++ b/drivers/hid/hid-gt683r.c
>> @@ -84,12 +84,13 @@ static void gt683r_brightness_set(struct led_classdev *led_cdev,
>> }
>> }
>>
>> -static ssize_t leds_mode_show(struct device *dev,
>> +static ssize_t mode_show(struct device *dev,
>> struct device_attribute *attr,
>> char *buf)
>> {
>> u8 sysfs_mode;
>> - struct hid_device *hdev = container_of(dev, struct hid_device, dev);
>> + struct hid_device *hdev = container_of(dev->parent,
>> + struct hid_device, dev);
>> struct gt683r_led *led = hid_get_drvdata(hdev);
>>
>> if (led->mode == GT683R_LED_NORMAL)
>> @@ -102,12 +103,13 @@ static ssize_t leds_mode_show(struct device *dev,
>> return scnprintf(buf, PAGE_SIZE, "%u\n", sysfs_mode);
>> }
>>
>> -static ssize_t leds_mode_store(struct device *dev,
>> +static ssize_t mode_store(struct device *dev,
>> struct device_attribute *attr,
>> const char *buf, size_t count)
>> {
>> u8 sysfs_mode;
>> - struct hid_device *hdev = container_of(dev, struct hid_device, dev);
>> + struct hid_device *hdev = container_of(dev->parent,
>> + struct hid_device, dev);
>> struct gt683r_led *led = hid_get_drvdata(hdev);
>>
>>
>> @@ -212,7 +214,22 @@ fail:
>> mutex_unlock(&led->lock);
>> }
>>
>> -static DEVICE_ATTR_RW(leds_mode);
>> +static DEVICE_ATTR_RW(mode);
>> +
>> +static struct attribute *gt683r_led_attrs[] = {
>> + &dev_attr_mode.attr,
>> + NULL
>> +};
>> +
>> +static const struct attribute_group gt683r_led_group = {
>> + .name = "gt683r",
>> + .attrs = gt683r_led_attrs,
>> +};
>> +
>> +static const struct attribute_group *gt683r_led_groups[] = {
>> + &gt683r_led_group,
>> + NULL
>> +};
>>
>> static int gt683r_led_probe(struct hid_device *hdev,
>> const struct hid_device_id *id)
>> @@ -261,6 +278,8 @@ static int gt683r_led_probe(struct hid_device *hdev,
>> led->led_devs[i].name = name;
>> led->led_devs[i].max_brightness = 1;
>> led->led_devs[i].brightness_set = gt683r_brightness_set;
>> + led->led_devs[i].groups = gt683r_led_groups;
>> +
>> ret = led_classdev_register(&hdev->dev, &led->led_devs[i]);
>> if (ret) {
>> hid_err(hdev, "could not register led device\n");
>> @@ -268,12 +287,6 @@ static int gt683r_led_probe(struct hid_device *hdev,
>> }
>> }
>>
>> - ret = device_create_file(&led->hdev->dev, &dev_attr_leds_mode);
>> - if (ret) {
>> - hid_err(hdev, "could not make mode attribute file\n");
>> - goto fail;
>> - }
>> -
>> return 0;
>>
>> fail:
>> @@ -288,7 +301,6 @@ static void gt683r_led_remove(struct hid_device *hdev)
>> int i;
>> struct gt683r_led *led = hid_get_drvdata(hdev);
>>
>> - device_remove_file(&hdev->dev, &dev_attr_leds_mode);
>> for (i = 0; i < GT683R_LED_COUNT; i++)
>> led_classdev_unregister(&led->led_devs[i]);
>> flush_work(&led->work);

2014-07-03 18:28:27

by Janne Kanniainen

[permalink] [raw]
Subject: Re: [PATCH 2/2 v6] HID: gt683r: move mode attribute to led-class devices

2014-07-03 21:17 GMT+03:00 Bryan Wu <[email protected]>:
> On Thu, Jul 3, 2014 at 10:40 AM, Johan Hovold <[email protected]> wrote:
>> On Thu, Jul 03, 2014 at 08:17:09PM +0300, Janne Kanniainen wrote:
>>> Move led_mode attribute from HID device to led-class devices and rename
>>> it msi_mode. This will also fix race condition by using
>>
>> There's a typo here (s/msi_mode/mode) but perhaps Bryan can just fix
>> that up before applying?
>>
>>> attribute-groups.
>>>
>>> Signed-off-by: Janne Kanniainen <[email protected]>
>>
>> Reviewed-by: Johan Hovold <[email protected]>
>>
>> Otherwise both patches (v6) are ready to be merged, Bryan.
>>
>> Thanks, Janne!
>>
>
> No problem. I fixed the typo and merged it.
>
> Thanks,
> -Bryan

Thanks again to both of you!

Janne