From patchwork Sat Mar 26 20:23:17 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Euchner X-Patchwork-Id: 8674881 Return-Path: X-Original-To: patchwork-linux-input@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 0E8C49F44D for ; Sat, 26 Mar 2016 20:23:34 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id D186D20279 for ; Sat, 26 Mar 2016 20:23:32 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id AA8A220219 for ; Sat, 26 Mar 2016 20:23:31 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753685AbcCZUX1 (ORCPT ); Sat, 26 Mar 2016 16:23:27 -0400 Received: from mail-wm0-f68.google.com ([74.125.82.68]:34335 "EHLO mail-wm0-f68.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753615AbcCZUX0 (ORCPT ); Sat, 26 Mar 2016 16:23:26 -0400 Received: by mail-wm0-f68.google.com with SMTP id p65so11773366wmp.1; Sat, 26 Mar 2016 13:23:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id; bh=pHoDF42znC25L8tDvZ5GsfSevTCt3OYffcUdlKD6V5o=; b=H9bTCJbqcD74Q0nbcccl68sUwVGtcVXeZR7VQgOE5tepbDl/p/g+f2PIKi++9w8Bev bEvUEH4ahSfNc5Mn8Fmk2EsXl9YfOY9wLVNjp6L3FGKhiMpj5SW28ykKcECoBJjbivCP pbTOwt21kvob6bQ7zHcdFedG0JymfOv35ATTYLhQ1lHKnqABUUBu6HVxjp3XZpubvNY/ jxsBRvpqNuEBYLqRsrcHtvgLPdNLtNNcTahArfTjr9C9clg9l/siBOH6orgEf1rcq1Bn q+ZKq1JpAeSTS8NWTMt66SLjagOJx/z0yZznxEn5ZZ9dhmFgFRwGGmD+CYCyVR7M7ExR XyxA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=pHoDF42znC25L8tDvZ5GsfSevTCt3OYffcUdlKD6V5o=; b=UkrqZk2RRvUgiexkDABvQYq5+3gUdoTEQ3Bnt/wCJ/Pj8lciz5Bh2njccjbDcusUov SDkYBM4vfso8QL8g8UPPp3RlR45Jjs/PgDTDwX7my/TSgEtO9/kAvzlr6lXWRTBD8PQK hGSPXZpmJi3WDjmgXFaX+KNmS5DjsmaPfO3HrEhw4DueFp9Y5rz3QGrOJAz4fDJ0Ld8o Ps6Drty9S3GBoEyCMPJWGOryeO9jlIgyStVmx3sitPqsXz2jXdHA0a+rZwi/5psHXv4d kQqz9HKsAdxoj1FYwHLN0mCgx0VMryu/6szxcowTYkvD/eZxqqxJho8olQZpYCT0F0Jo 6fyQ== X-Gm-Message-State: AD7BkJLmHvg8ufWIj5zeS9bxW2RwKdYsPKaBkyGSNSlvwFEhD9OIuBtX1LvD4t/uDYu2Fg== X-Received: by 10.28.224.132 with SMTP id x126mr3220779wmg.88.1459023804722; Sat, 26 Mar 2016 13:23:24 -0700 (PDT) Received: from x220.localdomain ([193.95.254.83]) by smtp.gmail.com with ESMTPSA id ll9sm17465675wjc.29.2016.03.26.13.23.23 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Sat, 26 Mar 2016 13:23:24 -0700 (PDT) From: Florian Euchner To: dmitry.torokhov@gmail.com Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, Florian Euchner Subject: [PATCH] Input: CM109: Fix handling of volume and mute buttons Date: Sat, 26 Mar 2016 21:23:17 +0100 Message-Id: <1459023797-6418-1-git-send-email-florian.euchner@gmail.com> X-Mailer: git-send-email 2.7.4 Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org X-Spam-Status: No, score=-7.8 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, T_DKIM_INVALID, UNPARSEABLE_RELAY autolearn=ham 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 The CM109 driver reported key press events of volume up / down and record / playback mute buttons, but release events only as soon as another button was pressed. Track state of volume buttons in order to report press and release events properly. For the record and playback mute buttons, only presses are registered by the CM109, therefore simulate press-n-release. This fixes the volume control buttons of various USB headsets. Signed-off-by: Florian Euchner Tested-by: Karsten Merker --- The CM109 datasheet states that bit 0 and 1 of HID_IR0 indicate if volume up / down have been pressed (1) or released (0). Bits 2 and 3 indicate a press-n-release for playback / record mute, there is no way to determine when the mute buttons were released. I contacted Alfred E. Heggestad, the original author of this driver, but he understandably couldn't comment on this issue as he didn't work with the CM109 for quite some time. I cannot test this patch with the original USB phones the CM109 driver was intended for, I don't own one of those and the CM109 ones (at least those mentioned in the driver) have become harder to obtain, but I'd be very surprised if this patch didn't also work with those. drivers/input/misc/cm109.c | 69 ++++++++++++++++++++++++++++++---------------- 1 file changed, 45 insertions(+), 24 deletions(-) diff --git a/drivers/input/misc/cm109.c b/drivers/input/misc/cm109.c index 9365535..e2c1a80 100644 --- a/drivers/input/misc/cm109.c +++ b/drivers/input/misc/cm109.c @@ -76,8 +76,8 @@ enum { BUZZER_ON = 1 << 5, - /* up to 256 normal keys, up to 16 special keys */ - KEYMAP_SIZE = 256 + 16, + /* up to 256 keys on the normal keymap */ + KEYMAP_SIZE = 256, }; /* CM109 protocol packet */ @@ -129,25 +129,14 @@ struct cm109_dev { int key_code; /* last reported key */ int keybit; /* 0=new scan 1,2,4,8=scan columns */ u8 gpi; /* Cached value of GPI (high nibble) */ + bool volup_cached; /* Cached state of volume up button */ + bool voldown_cached; /* Cached state of volume down button */ }; /****************************************************************************** * CM109 key interface *****************************************************************************/ -static unsigned short special_keymap(int code) -{ - if (code > 0xff) { - switch (code - 0xff) { - case RECORD_MUTE: return KEY_MUTE; - case PLAYBACK_MUTE: return KEY_MUTE; - case VOLUME_DOWN: return KEY_VOLUMEDOWN; - case VOLUME_UP: return KEY_VOLUMEUP; - } - } - return KEY_RESERVED; -} - /* Map device buttons to internal key events. * * The "up" and "down" keys, are symbolised by arrows on the button. @@ -191,7 +180,7 @@ static unsigned short keymap_kip1000(int scancode) case 0x48: return KEY_ESC; /* hangup */ case 0x28: return KEY_LEFT; /* IN */ case 0x18: return KEY_RIGHT; /* OUT */ - default: return special_keymap(scancode); + default: return KEY_RESERVED; } } @@ -224,7 +213,7 @@ static unsigned short keymap_gtalk(int scancode) case 0x28: return KEY_ESC; /* End (red handset) */ case 0x48: return KEY_UP; /* Menu up (rocker switch) */ case 0x88: return KEY_DOWN; /* Menu down (rocker switch) */ - default: return special_keymap(scancode); + default: return KEY_RESERVED; } } @@ -253,7 +242,7 @@ static unsigned short keymap_usbph01(int scancode) case 0x28: return KEY_ESC; /* hangup */ case 0x48: return KEY_LEFT; /* IN */ case 0x88: return KEY_RIGHT; /* OUT */ - default: return special_keymap(scancode); + default: return KEY_RESERVED; } } @@ -284,7 +273,7 @@ static unsigned short keymap_atcom(int scancode) case 0x28: return KEY_ESC; /* hangup */ case 0x48: return KEY_LEFT; /* left arrow */ case 0x88: return KEY_RIGHT; /* right arrow */ - default: return special_keymap(scancode); + default: return KEY_RESERVED; } } @@ -338,9 +327,13 @@ static void cm109_submit_buzz_toggle(struct cm109_dev *dev) static void cm109_urb_irq_callback(struct urb *urb) { struct cm109_dev *dev = urb->context; + struct input_dev *idev = dev->idev; const int status = urb->status; int error; + bool volup_pressed = !!(dev->irq_data->byte[HID_IR0] & VOLUME_UP); + bool voldown_pressed = !!(dev->irq_data->byte[HID_IR0] & VOLUME_DOWN); + dev_dbg(&dev->intf->dev, "### URB IRQ: [0x%02x 0x%02x 0x%02x 0x%02x] keybit=0x%02x\n", dev->irq_data->byte[0], dev->irq_data->byte[1], @@ -356,13 +349,35 @@ static void cm109_urb_irq_callback(struct urb *urb) goto out; } - /* Special keys */ - if (dev->irq_data->byte[HID_IR0] & 0x0f) { - const int code = (dev->irq_data->byte[HID_IR0] & 0x0f); - report_key(dev, dev->keymap[0xff + code]); + /* Report volume up / down button changes */ + if (volup_pressed != dev->volup_cached) { + input_report_key(idev, KEY_VOLUMEUP, volup_pressed); + input_sync(idev); + dev->volup_cached = volup_pressed; } - /* Scan key column */ + if (voldown_pressed != dev->voldown_cached) { + input_report_key(idev, KEY_VOLUMEDOWN, voldown_pressed); + input_sync(idev); + dev->voldown_cached = voldown_pressed; + } + + /* Playback / record mute buttons: simulate press-n-release */ + if (dev->irq_data->byte[HID_IR0] & PLAYBACK_MUTE) { + input_report_key(idev, KEY_MUTE, 1); + input_sync(idev); + input_report_key(idev, KEY_MUTE, 0); + input_sync(idev); + } + + if (dev->irq_data->byte[HID_IR0] & RECORD_MUTE) { + input_report_key(idev, KEY_MICMUTE, 1); + input_sync(idev); + input_report_key(idev, KEY_MICMUTE, 0); + input_sync(idev); + } + + /* Normal keymap: Scan key column */ if (dev->keybit == 0xf) { /* Any changes ? */ @@ -778,6 +793,12 @@ static int cm109_usb_probe(struct usb_interface *intf, } __clear_bit(KEY_RESERVED, input_dev->keybit); + /* register available special key events */ + __set_bit(KEY_VOLUMEUP, input_dev->keybit); + __set_bit(KEY_VOLUMEDOWN, input_dev->keybit); + __set_bit(KEY_MUTE, input_dev->keybit); + __set_bit(KEY_MICMUTE, input_dev->keybit); + error = input_register_device(dev->idev); if (error) goto err_out;