From patchwork Wed Mar 23 17:32:36 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Cl=C3=A9ment_VUCHENER?= X-Patchwork-Id: 8652471 Return-Path: X-Original-To: patchwork-linux-input@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id B41CBC0553 for ; Wed, 23 Mar 2016 17:32:49 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 40B9520211 for ; Wed, 23 Mar 2016 17:32:48 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 9A3C7201C8 for ; Wed, 23 Mar 2016 17:32:46 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756328AbcCWRcp (ORCPT ); Wed, 23 Mar 2016 13:32:45 -0400 Received: from mail-wm0-f66.google.com ([74.125.82.66]:35557 "EHLO mail-wm0-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753386AbcCWRco (ORCPT ); Wed, 23 Mar 2016 13:32:44 -0400 Received: by mail-wm0-f66.google.com with SMTP id r129so602058wmr.2; Wed, 23 Mar 2016 10:32:43 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=date:from:to:cc:subject:message-id:references:mime-version :content-disposition:content-transfer-encoding:in-reply-to :user-agent; bh=BbpEfDK1ZUK2nz0vlX4w4BH47dejgxL6sqxM4PemhYY=; b=H84iwGtmQaVlUZ9lYqjX2rxT9IONXv1/ONWXHH+E1KwHEGYhv7Zqohv6uMpNhAtmmL wUUtx7yXJvW42TqjDUZmF+5eYWesDuleN22BAGVfRyO3imeMvdTzOlG2eRLeVM3o3Z5Y rs7oUEVH05ISDQuexx0PmXmyTP2pah1h5WkYtvTT131jiquJUg4NEC96sW7p1yZieRA5 /a0Si+JIEyqF//S0FLNwedsn98FBVLxVQhk+ou9lS50bhEoal+ybjZ3VsIqUFXbV6OHm 2yGkglXtD50v1tj8ugj8Ej9hEEy9sd+f1P7QtyTwv8GaGJJ38ikfza1ddq2KPB6v7/p8 dicg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:date:from:to:cc:subject:message-id:references :mime-version:content-disposition:content-transfer-encoding :in-reply-to:user-agent; bh=BbpEfDK1ZUK2nz0vlX4w4BH47dejgxL6sqxM4PemhYY=; b=AkMFy1stM3E9ClUN41ZkRD6QBlvqhklB9iyFrNm9gMyyyO/dGKi5oO3XAAWlgNuMbb Xq7sxRwCgcBi2qatMMWRcChcy+VWrcrxx/fa2TRDkIYSgRSgddvrWAiGyN1xWsSeUpcH 4QUNNCPjqUw4p7YyTe2lPJdQHMs5D+iLsTY6N5y77SEN/9I0ueKJbgqAPKCgMcdUjWm/ g0Y5nIs2m0LgG3XKaWLJmVqVlK5ye2M6NssCpIVtys/urKj3V+O2Nu13ob5vRSVIyywi 00tH0iPnyQJXcXZjxoH9eS+JA5L40jBg8edmzTWYdHaIk6oowo4axUhP8pLLVZFUv8kS /ToA== X-Gm-Message-State: AD7BkJKQgicWVmxCzSDIexecxOm9hxWfLKSCCc1+8Qm/Rc316CVkjgtVbnozlqmjdGzMTQ== X-Received: by 10.28.126.131 with SMTP id z125mr5439211wmc.77.1458754362611; Wed, 23 Mar 2016 10:32:42 -0700 (PDT) Received: from arnab (ARennes-658-1-230-217.w92-139.abo.wanadoo.fr. [92.139.243.217]) by smtp.googlemail.com with ESMTPSA id i2sm3589566wje.22.2016.03.23.10.32.41 (version=TLSv1/SSLv3 cipher=OTHER); Wed, 23 Mar 2016 10:32:42 -0700 (PDT) Date: Wed, 23 Mar 2016 18:32:36 +0100 From: =?us-ascii?B?PT9VVEYtOD9xP0NsPUMzPUE5bWVudD0yMFZ1Y2hlbmVyPz0=?= To: Jiri Kosina Cc: Benjamin Tissoires , linux-kernel@vger.kernel.org, linux-input@vger.kernel.org Subject: [PATCH 1/2] HID: corsair: Remove all features using the USB protocol Message-ID: <6204914445b01d6379c3e3877014a7b11dc7c82b.1458744569.git.clement.vuchener@gmail.com> References: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: User-Agent: Mutt/1.5.24 (2015-08-30) Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org X-Spam-Status: No, score=-5.8 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, FROM_EXCESS_BASE64, RCVD_IN_DNSWL_HI, T_DKIM_INVALID, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Remove every use of USB control requests since it can be more easily done in user space. This removes the dependency on USB and LED subsystems. The simplyfied driver now only remaps Corsair usage codes. Signed-off-by: Clément Vuchener --- Documentation/ABI/testing/sysfs-driver-hid-corsair | 15 - drivers/hid/Kconfig | 2 +- drivers/hid/hid-corsair.c | 497 +-------------------- 3 files changed, 2 insertions(+), 512 deletions(-) delete mode 100644 Documentation/ABI/testing/sysfs-driver-hid-corsair diff --git a/Documentation/ABI/testing/sysfs-driver-hid-corsair b/Documentation/ABI/testing/sysfs-driver-hid-corsair deleted file mode 100644 index b8827f0..0000000 --- a/Documentation/ABI/testing/sysfs-driver-hid-corsair +++ /dev/null @@ -1,15 +0,0 @@ -What: /sys/bus/drivers/corsair//macro_mode -Date: August 2015 -KernelVersion: 4.2 -Contact: Clement Vuchener -Description: Get/set the current playback mode. "SW" for software mode - where G-keys triggers their regular key codes. "HW" for - hardware playback mode where the G-keys play their macro - from the on-board memory. - - -What: /sys/bus/drivers/corsair//current_profile -Date: August 2015 -KernelVersion: 4.2 -Contact: Clement Vuchener -Description: Get/set the current selected profile. Values are from 1 to 3. diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 4117225..43b018f 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -173,7 +173,7 @@ config HID_CHICONY config HID_CORSAIR tristate "Corsair devices" - depends on HID && USB && LEDS_CLASS + depends on HID ---help--- Support for Corsair devices that are not fully compliant with the HID standard. diff --git a/drivers/hid/hid-corsair.c b/drivers/hid/hid-corsair.c index 717704e..98f40aa 100644 --- a/drivers/hid/hid-corsair.c +++ b/drivers/hid/hid-corsair.c @@ -16,31 +16,9 @@ #include #include -#include -#include #include "hid-ids.h" -#define CORSAIR_USE_K90_MACRO (1<<0) -#define CORSAIR_USE_K90_BACKLIGHT (1<<1) - -struct k90_led { - struct led_classdev cdev; - int brightness; - struct work_struct work; - bool removed; -}; - -struct k90_drvdata { - struct k90_led record_led; -}; - -struct corsair_drvdata { - unsigned long quirks; - struct k90_drvdata *k90; - struct k90_led *backlight; -}; - #define K90_GKEY_COUNT 18 static int corsair_usage_to_gkey(unsigned int usage) @@ -119,474 +97,6 @@ MODULE_PARM_DESC(profilekey_codes, "Key codes for the profile buttons"); #define CORSAIR_USAGE_LIGHT_BRIGHT 0xfd #define CORSAIR_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 "::backlight" -#define K90_RECORD_LED_SUFFIX "::record" - -static enum led_brightness k90_backlight_get(struct led_classdev *led_cdev) -{ - int ret; - struct k90_led *led = container_of(led_cdev, struct k90_led, cdev); - 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 brightness; - char data[8]; - - 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) { - dev_warn(dev, "Failed to get K90 initial state (error %d).\n", - ret); - return -EIO; - } - brightness = data[4]; - if (brightness < 0 || brightness > 3) { - dev_warn(dev, - "Read invalid backlight brightness: %02hhx.\n", - data[4]); - return -EIO; - } - return brightness; -} - -static enum led_brightness k90_record_led_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; - struct usb_interface *usbif; - struct usb_device *usbdev; - - if (led->removed) - return; - - dev = led->cdev.dev->parent; - usbif = to_usb_interface(dev->parent); - 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; - struct usb_interface *usbif; - struct usb_device *usbdev; - int value; - - if (led->removed) - return; - - dev = led->cdev.dev->parent; - usbif = to_usb_interface(dev->parent); - usbdev = interface_to_usbdev(usbif); - - 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) -{ - int ret; - struct usb_interface *usbif = to_usb_interface(dev->parent); - struct usb_device *usbdev = interface_to_usbdev(usbif); - const char *macro_mode; - char data[8]; - - 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) { - dev_warn(dev, "Failed to get K90 initial mode (error %d).\n", - ret); - return -EIO; - } - - switch (data[0]) { - case K90_MACRO_MODE_HW: - macro_mode = "HW"; - break; - - case K90_MACRO_MODE_SW: - macro_mode = "SW"; - break; - default: - dev_warn(dev, "K90 in unknown mode: %02hhx.\n", - data[0]); - return -EIO; - } - - return snprintf(buf, PAGE_SIZE, "%s\n", macro_mode); -} - -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); - __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) { - dev_warn(dev, "Failed to set macro mode.\n"); - return ret; - } - - return count; -} - -static ssize_t k90_show_current_profile(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - int ret; - struct usb_interface *usbif = to_usb_interface(dev->parent); - struct usb_device *usbdev = interface_to_usbdev(usbif); - int current_profile; - char data[8]; - - 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) { - dev_warn(dev, "Failed to get K90 initial state (error %d).\n", - ret); - return -EIO; - } - current_profile = data[7]; - if (current_profile < 1 || current_profile > 3) { - dev_warn(dev, "Read invalid current profile: %02hhx.\n", - data[7]); - return -EIO; - } - - return snprintf(buf, PAGE_SIZE, "%d\n", 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); - 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) { - dev_warn(dev, "Failed to change current profile (error %d).\n", - ret); - return ret; - } - - 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_backlight(struct hid_device *dev) -{ - int ret; - struct corsair_drvdata *drvdata = hid_get_drvdata(dev); - size_t name_sz; - char *name; - - drvdata->backlight = kzalloc(sizeof(struct k90_led), GFP_KERNEL); - if (!drvdata->backlight) { - ret = -ENOMEM; - goto fail_backlight_alloc; - } - - name_sz = - strlen(dev_name(&dev->dev)) + sizeof(K90_BACKLIGHT_LED_SUFFIX); - name = kzalloc(name_sz, GFP_KERNEL); - if (!name) { - ret = -ENOMEM; - goto fail_name_alloc; - } - snprintf(name, name_sz, "%s" K90_BACKLIGHT_LED_SUFFIX, - dev_name(&dev->dev)); - drvdata->backlight->removed = false; - drvdata->backlight->cdev.name = name; - drvdata->backlight->cdev.max_brightness = 3; - drvdata->backlight->cdev.brightness_set = k90_brightness_set; - drvdata->backlight->cdev.brightness_get = k90_backlight_get; - INIT_WORK(&drvdata->backlight->work, k90_backlight_work); - ret = led_classdev_register(&dev->dev, &drvdata->backlight->cdev); - if (ret != 0) - goto fail_register_cdev; - - return 0; - -fail_register_cdev: - kfree(drvdata->backlight->cdev.name); -fail_name_alloc: - kfree(drvdata->backlight); - drvdata->backlight = NULL; -fail_backlight_alloc: - return ret; -} - -static int k90_init_macro_functions(struct hid_device *dev) -{ - int ret; - struct corsair_drvdata *drvdata = hid_get_drvdata(dev); - struct k90_drvdata *k90; - size_t name_sz; - char *name; - - k90 = kzalloc(sizeof(struct k90_drvdata), GFP_KERNEL); - if (!k90) { - ret = -ENOMEM; - goto fail_drvdata; - } - drvdata->k90 = k90; - - /* Init LED device for record LED */ - name_sz = strlen(dev_name(&dev->dev)) + sizeof(K90_RECORD_LED_SUFFIX); - name = kzalloc(name_sz, GFP_KERNEL); - if (!name) { - ret = -ENOMEM; - goto fail_record_led_alloc; - } - snprintf(name, name_sz, "%s" K90_RECORD_LED_SUFFIX, - dev_name(&dev->dev)); - k90->record_led.removed = false; - k90->record_led.cdev.name = name; - k90->record_led.cdev.max_brightness = 1; - k90->record_led.cdev.brightness_set = k90_brightness_set; - k90->record_led.cdev.brightness_get = k90_record_led_get; - INIT_WORK(&k90->record_led.work, k90_record_led_work); - k90->record_led.brightness = 0; - ret = led_classdev_register(&dev->dev, &k90->record_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: - k90->record_led.removed = true; - led_classdev_unregister(&k90->record_led.cdev); - cancel_work_sync(&k90->record_led.work); -fail_record_led: - kfree(k90->record_led.cdev.name); -fail_record_led_alloc: - kfree(k90); -fail_drvdata: - drvdata->k90 = NULL; - return ret; -} - -static void k90_cleanup_backlight(struct hid_device *dev) -{ - struct corsair_drvdata *drvdata = hid_get_drvdata(dev); - - if (drvdata->backlight) { - drvdata->backlight->removed = true; - led_classdev_unregister(&drvdata->backlight->cdev); - cancel_work_sync(&drvdata->backlight->work); - kfree(drvdata->backlight->cdev.name); - kfree(drvdata->backlight); - } -} - -static void k90_cleanup_macro_functions(struct hid_device *dev) -{ - struct corsair_drvdata *drvdata = hid_get_drvdata(dev); - struct k90_drvdata *k90 = drvdata->k90; - - if (k90) { - sysfs_remove_group(&dev->dev.kobj, &k90_attr_group); - - k90->record_led.removed = true; - led_classdev_unregister(&k90->record_led.cdev); - cancel_work_sync(&k90->record_led.work); - kfree(k90->record_led.cdev.name); - - kfree(k90); - } -} - -static int corsair_probe(struct hid_device *dev, const struct hid_device_id *id) -{ - int ret; - unsigned long quirks = id->driver_data; - struct corsair_drvdata *drvdata; - struct usb_interface *usbif = to_usb_interface(dev->dev.parent); - - drvdata = devm_kzalloc(&dev->dev, sizeof(struct corsair_drvdata), - GFP_KERNEL); - if (drvdata == NULL) - return -ENOMEM; - drvdata->quirks = quirks; - hid_set_drvdata(dev, drvdata); - - 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) { - if (quirks & CORSAIR_USE_K90_MACRO) { - ret = k90_init_macro_functions(dev); - if (ret != 0) - hid_warn(dev, "Failed to initialize K90 macro functions.\n"); - } - if (quirks & CORSAIR_USE_K90_BACKLIGHT) { - ret = k90_init_backlight(dev); - if (ret != 0) - hid_warn(dev, "Failed to initialize K90 backlight.\n"); - } - } - - return 0; -} - -static void corsair_remove(struct hid_device *dev) -{ - k90_cleanup_macro_functions(dev); - k90_cleanup_backlight(dev); - - hid_hw_stop(dev); -} - -static int corsair_event(struct hid_device *dev, struct hid_field *field, - struct hid_usage *usage, __s32 value) -{ - struct corsair_drvdata *drvdata = hid_get_drvdata(dev); - - if (!drvdata->k90) - return 0; - - switch (usage->hid & HID_USAGE) { - case CORSAIR_USAGE_MACRO_RECORD_START: - drvdata->k90->record_led.brightness = 1; - break; - case CORSAIR_USAGE_MACRO_RECORD_STOP: - drvdata->k90->record_led.brightness = 0; - break; - default: - break; - } - - return 0; -} - static int corsair_input_mapping(struct hid_device *dev, struct hid_input *input, struct hid_field *field, @@ -641,9 +151,7 @@ static int corsair_input_mapping(struct hid_device *dev, } static const struct hid_device_id corsair_devices[] = { - { HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K90), - .driver_data = CORSAIR_USE_K90_MACRO | - CORSAIR_USE_K90_BACKLIGHT }, + { HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K90) }, {} }; @@ -652,9 +160,6 @@ MODULE_DEVICE_TABLE(hid, corsair_devices); static struct hid_driver corsair_driver = { .name = "corsair", .id_table = corsair_devices, - .probe = corsair_probe, - .event = corsair_event, - .remove = corsair_remove, .input_mapping = corsair_input_mapping, };