On Mon, Sep 07, 2015 at 12:08:17PM +0530, Pranay Srivastava wrote:
> On Sun, Sep 6, 2015 at 11:18 PM, Clément Vuchener
> <[email protected]> wrote:
> > Hello,
> >
> > I am trying to write a driver that uses LED class devices using works for 
> > setting the LED brightness but I am not sure of how to unregister the 
> > devices.
> >
> > I have been using code like this:
> >         led_classdev_unregister(&drvdata->backlight.cdev);
> >         cancel_work_sync(&drvdata->backlight.work);
> > trying with both flush_work or cancel_work_sync as I have seen it in other 
> > drivers.
> >
> > Using flush_work, the kernel oops in my work function when I unplug the 
> > device. cancel_work_sync seems to fix that, but I am not sure it will work 
> > every time. I would like to understand what happens and if I am doing 
> > something wrong, to be sure it will not break in some different setup.
> 
> Can you post the backtrace?
> 

I could not get it with my patched kernel (I must be missing some config 
option) so I used the code as a module on my fedora 22 (4.1.6) kernel.

general protection fault: 0000 [#1] SMP 
Modules linked in: hid_corsair_k90(OE) bnep bluetooth nf_nat_h323 
nf_conntrack_h323 nf_nat_pptp nf_nat_proto_gre nf_conntrack_pptp 
nf_conntrack_proto_g
 snd_hda_codec_hdmi coretemp arc4 kvm_intel snd_hda_codec_realtek iwldvm kvm 
snd_hda_codec_generic mac80211 snd_hda_intel snd_hda_controller snd_hda_co
CPU: 2 PID: 491 Comm: kworker/2:3 Tainted: G           OE   
4.1.6-200.fc22.x86_64 #1
Hardware name: CLEVO CO.                        W350ET/W350ET, BIOS 1.02.21PM 
v3 07/01/2013
Workqueue: events k90_record_led_work [hid_corsair_k90]
task: ffff880223bd4f00 ti: ffff8800c92a0000 task.ti: ffff8800c92a0000
RIP: 0010:[<ffffffff814e8816>]  [<ffffffff814e8816>] __dev_printk+0x26/0x90
RSP: 0018:ffff8800c92a3d48  EFLAGS: 00010202
RAX: 657079740000009d RBX: ffff8801fcee7800 RCX: 000000000001a2e1
RDX: ffff8800c92a3d58 RSI: ffff8801fcee7800 RDI: ffffffff81a2673f
RBP: ffff8800c92a3d48 R08: 0000001400730102 R09: ffff8800c92a3d58
R10: ffffffff81578c4b R11: 0000000000000000 R12: ffff88022f317000
R13: ffff88022f31b900 R14: 0000000000000080 R15: ffff8801fcc7d320
FS:  0000000000000000(0000) GS:ffff88022f300000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000000002eee850 CR3: 0000000002c0b000 CR4: 00000000001406e0
Stack:
 ffff8800c92a3db8 ffffffff814e8bd2 ffffffffa09701f8 ffff8800c92a3d68
 ffff880100000010 ffff8800c92a3dc8 ffff8800c92a3d88 000000002440e468
 0000000000000000 ffff8801fcee7800 00000000ffffffed 000000000001a2e1
Call Trace:
 [<ffffffff814e8bd2>] dev_warn+0x62/0x80
 [<ffffffffa096f44c>] k90_record_led_work+0x8c/0xa0 [hid_corsair_k90]
 [<ffffffff8179da31>] ? __schedule+0x241/0x720
 [<ffffffff810baadb>] process_one_work+0x1bb/0x410
 [<ffffffff810baeec>] worker_thread+0x1bc/0x480
 [<ffffffff810bad30>] ? process_one_work+0x410/0x410
 [<ffffffff810bad30>] ? process_one_work+0x410/0x410
 [<ffffffff810c0bf8>] kthread+0xd8/0xf0
 [<ffffffff810c0b20>] ? kthread_worker_fn+0x180/0x180
 [<ffffffff817a2322>] ret_from_fork+0x42/0x70
 [<ffffffff810c0b20>] ? kthread_worker_fn+0x180/0x180
Code: 00 00 00 00 00 0f 1f 44 00 00 55 48 85 f6 49 89 d1 48 89 e5 74 60 4c 8b 
46 50 4d 85 c0 74 26 48 8b 86 90 00 00 00 48 85 c0 74 2a <48> 8b 08 0f be
RIP  [<ffffffff814e8816>] __dev_printk+0x26/0x90

> >
> > Another problem I have with this unregistering is that the LED brightness 
> > is set to zero when unregistering when the hardware is supposed to remember 
> > the last settings. Thus the LED state is reset when detaching and 
> > reattaching the driver.
> >
> > Below is the full code from my driver, if you need it:
> >
> > /*
> >  * HID driver for Corsair devices
> >  *
> >  * Supported devices:
> >  *  - Vengeance K90 Keyboard
> >  *
> >  * Copyright (c) 2015 Clement Vuchener
> >  */
> >
> > /*
> >  * 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.
> >  */
> >
> > #include <linux/hid.h>
> > #include <linux/module.h>
> > #include <linux/usb.h>
> > #include <linux/leds.h>
> >
> > #include "hid-ids.h"
> >
> > struct k90_led {
> >         struct led_classdev cdev;
> >         int brightness;
> >         struct work_struct work;
> > };
> >
> > struct k90_drvdata {
> >         int current_profile;
> >         int macro_mode;
> >         int meta_locked;
> >         struct k90_led backlight;
> >         struct k90_led record_led;
> > };
> >
> > #define K90_GKEY_COUNT  18
> >
> > static int k90_usage_to_gkey(unsigned int usage)
> > {
> >         /* G1 (0xd0) to G16 (0xdf) */
> >         if (usage >= 0xd0 && usage <= 0xdf)
> >                 return usage - 0xd0 + 1;
> >         /* G17 (0xe8) to G18 (0xe9) */
> >         if (usage >= 0xe8 && usage <= 0xe9)
> >                 return usage - 0xe8 + 17;
> >         return 0;
> > }
> >
> > static unsigned short k90_gkey_map[K90_GKEY_COUNT] = {
> >         BTN_TRIGGER_HAPPY1,
> >         BTN_TRIGGER_HAPPY2,
> >         BTN_TRIGGER_HAPPY3,
> >         BTN_TRIGGER_HAPPY4,
> >         BTN_TRIGGER_HAPPY5,
> >         BTN_TRIGGER_HAPPY6,
> >         BTN_TRIGGER_HAPPY7,
> >         BTN_TRIGGER_HAPPY8,
> >         BTN_TRIGGER_HAPPY9,
> >         BTN_TRIGGER_HAPPY10,
> >         BTN_TRIGGER_HAPPY11,
> >         BTN_TRIGGER_HAPPY12,
> >         BTN_TRIGGER_HAPPY13,
> >         BTN_TRIGGER_HAPPY14,
> >         BTN_TRIGGER_HAPPY15,
> >         BTN_TRIGGER_HAPPY16,
> >         BTN_TRIGGER_HAPPY17,
> >         BTN_TRIGGER_HAPPY18,
> > };
> >
> > module_param_array_named(gkey_codes, k90_gkey_map, ushort, NULL, S_IRUGO);
> >
> > #define K90_USAGE_SPECIAL_MIN 0xf0
> > #define K90_USAGE_SPECIAL_MAX 0xff
> >
> > #define K90_USAGE_MACRO_RECORD_START 0xf6
> > #define K90_USAGE_MACRO_RECORD_STOP 0xf7
> >
> > #define K90_USAGE_PROFILE 0xf1
> > #define K90_USAGE_M1 0xf1
> > #define K90_USAGE_M2 0xf2
> > #define K90_USAGE_M3 0xf3
> > #define K90_USAGE_PROFILE_MAX 0xf3
> >
> > #define K90_USAGE_META_OFF 0xf4
> > #define K90_USAGE_META_ON  0xf5
> >
> > #define K90_USAGE_LIGHT 0xfa
> > #define K90_USAGE_LIGHT_OFF 0xfa
> > #define K90_USAGE_LIGHT_DIM 0xfb
> > #define K90_USAGE_LIGHT_MEDIUM 0xfc
> > #define K90_USAGE_LIGHT_BRIGHT 0xfd
> > #define K90_USAGE_LIGHT_MAX 0xfd
> >
> > /* USB control protocol */
> >
> > #define K90_REQUEST_BRIGHTNESS 49
> > #define K90_REQUEST_MACRO_MODE 2
> > #define K90_REQUEST_STATUS 4
> > #define K90_REQUEST_GET_MODE 5
> > #define K90_REQUEST_PROFILE 20
> >
> > #define K90_MACRO_MODE_SW 0x0030
> > #define K90_MACRO_MODE_HW 0x0001
> >
> > #define K90_MACRO_LED_ON  0x0020
> > #define K90_MACRO_LED_OFF 0x0040
> >
> > /*
> >  * LED class devices
> >  */
> >
> > #define K90_BACKLIGHT_LED_SUFFIX ":blue:backlight"
> > #define K90_RECORD_LED_SUFFIX ":red:record"
> >
> > static enum led_brightness k90_brightness_get(struct led_classdev *led_cdev)
> > {
> >         struct k90_led *led = container_of(led_cdev, struct k90_led, cdev);
> >
> >         return led->brightness;
> > }
> >
> > static void k90_brightness_set(struct led_classdev *led_cdev,
> >                                enum led_brightness brightness)
> > {
> >         struct k90_led *led = container_of(led_cdev, struct k90_led, cdev);
> >
> >         led->brightness = brightness;
> >         schedule_work(&led->work);
> > }
> >
> > static void k90_backlight_work(struct work_struct *work)
> > {
> >         int ret;
> >         struct k90_led *led = container_of(work, struct k90_led, work);
> >         struct device *dev = led->cdev.dev->parent;
> >         struct usb_interface *usbif = to_usb_interface(dev->parent);
> >         struct usb_device *usbdev = interface_to_usbdev(usbif);
> >
> >         ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0),
> >                               K90_REQUEST_BRIGHTNESS,
> >                               USB_DIR_OUT | USB_TYPE_VENDOR |
> >                               USB_RECIP_DEVICE, led->brightness, 0,
> >                               NULL, 0, USB_CTRL_SET_TIMEOUT);
> >         if (ret != 0)
> >                 dev_warn(dev, "Failed to set backlight brightness (error: 
> > %d).\n",
> >                          ret);
> > }
> >
> > static void k90_record_led_work(struct work_struct *work)
> > {
> >         int ret;
> >         struct k90_led *led = container_of(work, struct k90_led, work);
> >         struct device *dev = led->cdev.dev->parent;
> >         struct usb_interface *usbif = to_usb_interface(dev->parent);
> >         struct usb_device *usbdev = interface_to_usbdev(usbif);
> >         int value;
> >
> >         if (led->brightness > 0)
> >                 value = K90_MACRO_LED_ON;
> >         else
> >                 value = K90_MACRO_LED_OFF;
> >
> >         ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0),
> >                               K90_REQUEST_MACRO_MODE,
> >                               USB_DIR_OUT | USB_TYPE_VENDOR |
> >                               USB_RECIP_DEVICE, value, 0, NULL, 0,
> >                               USB_CTRL_SET_TIMEOUT);
> >         if (ret != 0)
> >                 dev_warn(dev, "Failed to set record LED state (error: 
> > %d).\n",
> >                          ret);
> > }
> >
> > /*
> >  * Keyboard attributes
> >  */
> >
> > static ssize_t k90_show_macro_mode(struct device *dev,
> >                                    struct device_attribute *attr, char *buf)
> > {
> >         struct k90_drvdata *drvdata = dev_get_drvdata(dev);
> >
> >         return snprintf(buf, PAGE_SIZE, "%s\n",
> >                         (drvdata->macro_mode ? "HW" : "SW"));
> > }
> >
> > static ssize_t k90_store_macro_mode(struct device *dev,
> >                                     struct device_attribute *attr,
> >                                     const char *buf, size_t count)
> > {
> >         int ret;
> >         struct usb_interface *usbif = to_usb_interface(dev->parent);
> >         struct usb_device *usbdev = interface_to_usbdev(usbif);
> >         struct k90_drvdata *drvdata = dev_get_drvdata(dev);
> >         __u16 value;
> >
> >         if (strncmp(buf, "SW", 2) == 0)
> >                 value = K90_MACRO_MODE_SW;
> >         else if (strncmp(buf, "HW", 2) == 0)
> >                 value = K90_MACRO_MODE_HW;
> >         else
> >                 return -EINVAL;
> >
> >         ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0),
> >                               K90_REQUEST_MACRO_MODE,
> >                               USB_DIR_OUT | USB_TYPE_VENDOR |
> >                               USB_RECIP_DEVICE, value, 0, NULL, 0,
> >                               USB_CTRL_SET_TIMEOUT);
> >         if (ret != 0)
> >                 return ret;
> >
> >         drvdata->macro_mode = (value == K90_MACRO_MODE_HW);
> >
> >         return count;
> > }
> >
> > static ssize_t k90_show_current_profile(struct device *dev,
> >                                         struct device_attribute *attr,
> >                                         char *buf)
> > {
> >         struct k90_drvdata *drvdata = dev_get_drvdata(dev);
> >
> >         return snprintf(buf, PAGE_SIZE, "%d\n", drvdata->current_profile);
> > }
> >
> > static ssize_t k90_store_current_profile(struct device *dev,
> >                                          struct device_attribute *attr,
> >                                          const char *buf, size_t count)
> > {
> >         int ret;
> >         struct usb_interface *usbif = to_usb_interface(dev->parent);
> >         struct usb_device *usbdev = interface_to_usbdev(usbif);
> >         struct k90_drvdata *drvdata = dev_get_drvdata(dev);
> >         int profile;
> >
> >         if (kstrtoint(buf, 10, &profile))
> >                 return -EINVAL;
> >         if (profile < 1 || profile > 3)
> >                 return -EINVAL;
> >
> >         ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0),
> >                               K90_REQUEST_PROFILE,
> >                               USB_DIR_OUT | USB_TYPE_VENDOR |
> >                               USB_RECIP_DEVICE, profile, 0, NULL, 0,
> >                               USB_CTRL_SET_TIMEOUT);
> >         if (ret != 0)
> >                 return ret;
> >
> >         drvdata->current_profile = profile;
> >
> >         return count;
> > }
> >
> > static DEVICE_ATTR(macro_mode, 0644, k90_show_macro_mode, 
> > k90_store_macro_mode);
> > static DEVICE_ATTR(current_profile, 0644, k90_show_current_profile,
> >                    k90_store_current_profile);
> >
> > static struct attribute *k90_attrs[] = {
> >         &dev_attr_macro_mode.attr,
> >         &dev_attr_current_profile.attr,
> >         NULL
> > };
> >
> > static const struct attribute_group k90_attr_group = {
> >         .attrs = k90_attrs,
> > };
> >
> > /*
> >  * Driver functions
> >  */
> >
> > static int k90_init_special_functions(struct hid_device *dev)
> > {
> >         int ret;
> >         struct usb_interface *usbif = to_usb_interface(dev->dev.parent);
> >         struct usb_device *usbdev = interface_to_usbdev(usbif);
> >         char data[8];
> >         struct k90_drvdata *drvdata =
> >             kzalloc(sizeof(struct k90_drvdata), GFP_KERNEL);
> >         size_t name_sz;
> >         char *name;
> >         struct k90_led *led;
> >
> >         if (!drvdata) {
> >                 ret = -ENOMEM;
> >                 goto fail_drvdata;
> >         }
> >         hid_set_drvdata(dev, drvdata);
> >
> >         /* Get current status */
> >         ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0),
> >                               K90_REQUEST_STATUS,
> >                               USB_DIR_IN | USB_TYPE_VENDOR |
> >                               USB_RECIP_DEVICE, 0, 0, data, 8,
> >                               USB_CTRL_SET_TIMEOUT);
> >         if (ret < 0) {
> >                 hid_warn(dev, "Failed to get K90 initial state (error 
> > %d).\n",
> >                          ret);
> >                 drvdata->backlight.brightness = 0;
> >                 drvdata->current_profile = 1;
> >         } else {
> >                 drvdata->backlight.brightness = data[4];
> >                 drvdata->current_profile = data[7];
> >         }
> >         /* Get current mode */
> >         ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0),
> >                               K90_REQUEST_GET_MODE,
> >                               USB_DIR_IN | USB_TYPE_VENDOR |
> >                               USB_RECIP_DEVICE, 0, 0, data, 2,
> >                               USB_CTRL_SET_TIMEOUT);
> >         if (ret < 0)
> >                 hid_warn(dev, "Failed to get K90 initial mode (error 
> > %d).\n",
> >                          ret);
> >         else {
> >                 switch (data[0]) {
> >                 case K90_MACRO_MODE_HW:
> >                         drvdata->macro_mode = 1;
> >                         break;
> >                 case K90_MACRO_MODE_SW:
> >                         drvdata->macro_mode = 0;
> >                         break;
> >                 default:
> >                         hid_warn(dev, "K90 in unknown mode: %02x.\n",
> >                                  data[0]);
> >                 }
> >         }
> >
> >         /* Init LED device for backlight */
> >         name_sz =
> >             strlen(dev_name(&dev->dev)) + sizeof(K90_BACKLIGHT_LED_SUFFIX);
> >         name = devm_kzalloc(&dev->dev, name_sz, GFP_KERNEL);
> >         if (!name) {
> >                 ret = -ENOMEM;
> >                 goto fail_backlight;
> >         }
> >         snprintf(name, name_sz, "%s" K90_BACKLIGHT_LED_SUFFIX,
> >                  dev_name(&dev->dev));
> >         led = &drvdata->backlight;
> >         led->cdev.name = name;
> >         led->cdev.max_brightness = 3;
> >         led->cdev.brightness_set = k90_brightness_set;
> >         led->cdev.brightness_get = k90_brightness_get;
> >         INIT_WORK(&led->work, k90_backlight_work);
> >         ret = led_classdev_register(&dev->dev, &led->cdev);
> >         if (ret != 0)
> >                 goto fail_backlight;
> >
> >         /* Init LED device for record LED */
> >         name_sz = strlen(dev_name(&dev->dev)) + 
> > sizeof(K90_RECORD_LED_SUFFIX);
> >         name = devm_kzalloc(&dev->dev, name_sz, GFP_KERNEL);
> >         if (!name) {
> >                 ret = -ENOMEM;
> >                 goto fail_record_led;
> >         }
> >         snprintf(name, name_sz, "%s" K90_RECORD_LED_SUFFIX,
> >                  dev_name(&dev->dev));
> >         led = &drvdata->record_led;
> >         led->cdev.name = name;
> >         led->cdev.max_brightness = 1;
> >         led->cdev.brightness_set = k90_brightness_set;
> >         led->cdev.brightness_get = k90_brightness_get;
> >         INIT_WORK(&led->work, k90_record_led_work);
> >         ret = led_classdev_register(&dev->dev, &led->cdev);
> >         if (ret != 0)
> >                 goto fail_record_led;
> >
> >         /* Init attributes */
> >         ret = sysfs_create_group(&dev->dev.kobj, &k90_attr_group);
> >         if (ret != 0)
> >                 goto fail_sysfs;
> >
> >         return 0;
> >
> > fail_sysfs:
> >         led_classdev_unregister(&drvdata->record_led.cdev);
> >         cancel_work_sync(&drvdata->record_led.work);
> > fail_record_led:
> >         led_classdev_unregister(&drvdata->backlight.cdev);
> >         cancel_work_sync(&drvdata->backlight.work);
> > fail_backlight:
> >         kfree(drvdata);
> > fail_drvdata:
> >         hid_set_drvdata(dev, NULL);
> >         return ret;
> > }
> >
> > static void k90_cleanup_special_functions(struct hid_device *dev)
> > {
> >         struct k90_drvdata *drvdata = hid_get_drvdata(dev);
> >
> >         if (drvdata) {
> >                 sysfs_remove_group(&dev->dev.kobj, &k90_attr_group);
> >                 led_classdev_unregister(&drvdata->record_led.cdev);
> >                 led_classdev_unregister(&drvdata->backlight.cdev);
> 
> flush_work/cancel_work before doing unregistering?
> 
If led_classdev_unregister generates work (setting the brightness to zero), 
there will still be work in the workqueue after the cleanup. I don't understand 
why the work should be flushed/cancelled while the led device is still active.
> >                 cancel_work_sync(&drvdata->record_led.work);
> >                 cancel_work_sync(&drvdata->backlight.work);
> >                 kfree(drvdata);
> >         }
> > }
> >
> > static int k90_probe(struct hid_device *dev, const struct hid_device_id *id)
> > {
> >         int ret;
> >         struct usb_interface *usbif = to_usb_interface(dev->dev.parent);
> >
> >         ret = hid_parse(dev);
> >         if (ret != 0) {
> >                 hid_err(dev, "parse failed\n");
> >                 return ret;
> >         }
> >         ret = hid_hw_start(dev, HID_CONNECT_DEFAULT);
> >         if (ret != 0) {
> >                 hid_err(dev, "hw start failed\n");
> >                 return ret;
> >         }
> >
> >         if (usbif->cur_altsetting->desc.bInterfaceNumber == 0) {
> >                 ret = k90_init_special_functions(dev);
> >                 if (ret != 0)
> >                         hid_warn(dev, "Failed to initialize K90 special 
> > functions.\n");
> >         } else
> >                 hid_set_drvdata(dev, NULL);
> >
> >         return 0;
> > }
> >
> > static void k90_remove(struct hid_device *dev)
> > {
> >         struct usb_interface *usbif = to_usb_interface(dev->dev.parent);
> >
> >         if (usbif->cur_altsetting->desc.bInterfaceNumber == 0)
> >                 k90_cleanup_special_functions(dev);
> >
> >         hid_hw_stop(dev);
> > }
> >
> > static int k90_event(struct hid_device *dev, struct hid_field *field,
> >                      struct hid_usage *usage, __s32 value)
> > {
> >         struct k90_drvdata *drvdata = hid_get_drvdata(dev);
> >
> >         if (!drvdata)
> >                 return 0;
> >
> >         switch (usage->hid & HID_USAGE) {
> >         case K90_USAGE_MACRO_RECORD_START:
> >                 drvdata->record_led.brightness = 1;
> >                 break;
> >         case K90_USAGE_MACRO_RECORD_STOP:
> >                 drvdata->record_led.brightness = 0;
> >                 break;
> >         case K90_USAGE_M1:
> >         case K90_USAGE_M2:
> >         case K90_USAGE_M3:
> >                 drvdata->current_profile =
> >                     (usage->hid & HID_USAGE) - K90_USAGE_PROFILE + 1;
> >                 break;
> >         case K90_USAGE_META_OFF:
> >                 drvdata->meta_locked = 0;
> >                 break;
> >         case K90_USAGE_META_ON:
> >                 drvdata->meta_locked = 1;
> >                 break;
> >         case K90_USAGE_LIGHT_OFF:
> >         case K90_USAGE_LIGHT_DIM:
> >         case K90_USAGE_LIGHT_MEDIUM:
> >         case K90_USAGE_LIGHT_BRIGHT:
> >                 drvdata->backlight.brightness = (usage->hid & HID_USAGE) -
> >                     K90_USAGE_LIGHT;
> >                 break;
> >         default:
> >                 break;
> >         }
> >
> >         return 0;
> > }
> >
> > static int k90_input_mapping(struct hid_device *dev, struct hid_input 
> > *input,
> >                              struct hid_field *field, struct hid_usage 
> > *usage,
> >                              unsigned long **bit, int *max)
> > {
> >         int gkey;
> >
> >         gkey = k90_usage_to_gkey(usage->hid & HID_USAGE);
> >         if (gkey != 0) {
> >                 hid_map_usage_clear(input, usage, bit, max, EV_KEY,
> >                                     k90_gkey_map[gkey - 1]);
> >                 return 1;
> >         }
> >         if ((usage->hid & HID_USAGE) >= K90_USAGE_SPECIAL_MIN &&
> >             (usage->hid & HID_USAGE) <= K90_USAGE_SPECIAL_MAX)
> >                 return -1;
> >
> >         return 0;
> > }
> >
> > static const struct hid_device_id k90_devices[] = {
> >         { HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K90) 
> > },
> >         {}
> > };
> >
> > MODULE_DEVICE_TABLE(hid, k90_devices);
> >
> > static struct hid_driver k90_driver = {
> >         .name = "k90",
> >         .id_table = k90_devices,
> >         .probe = k90_probe,
> >         .event = k90_event,
> >         .remove = k90_remove,
> >         .input_mapping = k90_input_mapping,
> > };
> >
> > static int __init k90_init(void)
> > {
> >         return hid_register_driver(&k90_driver);
> > }
> >
> > static void k90_exit(void)
> > {
> >         hid_unregister_driver(&k90_driver);
> > }
> >
> > module_init(k90_init);
> > module_exit(k90_exit);
> >
> > MODULE_LICENSE("GPL");
> > MODULE_AUTHOR("Clement Vuchener");
> > MODULE_DESCRIPTION("HID driver for Corsair Vengeance K90 Keyboard");
> >
> > _______________________________________________
> > Kernelnewbies mailing list
> > [email protected]
> > http://lists.kernelnewbies.org/mailman/listinfo/kernelnewbies
> 
> 
> 
> -- 
>         ---P.K.S
--
To unsubscribe from this list: send the line "unsubscribe linux-leds" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to