From patchwork Wed Jun 7 06:59:34 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Torokhov X-Patchwork-Id: 9770671 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id B727060234 for ; Wed, 7 Jun 2017 07:01:31 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id A291628472 for ; Wed, 7 Jun 2017 07:01:31 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 97A41284ED; Wed, 7 Jun 2017 07:01:31 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.8 required=2.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED,FREEMAIL_FROM,RCVD_IN_DNSWL_HI,T_DKIM_INVALID autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 1E35628472 for ; Wed, 7 Jun 2017 07:01:31 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751563AbdFGHAr (ORCPT ); Wed, 7 Jun 2017 03:00:47 -0400 Received: from mail-pg0-f67.google.com ([74.125.83.67]:34056 "EHLO mail-pg0-f67.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751522AbdFGG7p (ORCPT ); Wed, 7 Jun 2017 02:59:45 -0400 Received: by mail-pg0-f67.google.com with SMTP id v14so634465pgn.1; Tue, 06 Jun 2017 23:59:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=mrl+2GzVqE/y34IsMGek1ssIcyvE3LAPjP+5xjYZ3SU=; b=EgBvy/8FU1uZf0xbvBTEJpp2w/Je9cPWefhu6ZkpsiIkkhqoyh9BB29ERbm+4UKmUn l8nKhHR8ezQ9hZT4s2bG458wI9KKIOxUFsxwtRW1THWEzkvTkjIzdWDiA2NorzNgqST5 U1o1OYKGgYWab9boLX9MbG/cx7KKeovP3gFl/xIYQSZElIohvgNBvr6ziWso4pTSe67Y F/zO8boYk3Ea2Qy4SmTgh6jBxhC0/8rO8YphyaMaykacsYEmmc23JiO3vCs/IAMJ4zyx 0RgOSatnxqZDRt9wED/WnwLDfyoKdDjDFlYHk8/kN0HBMvo5Gqip8yisJSaARwN6YKp0 qR/Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=mrl+2GzVqE/y34IsMGek1ssIcyvE3LAPjP+5xjYZ3SU=; b=hRs4IfW14it7m+30yZExjF8Tkk3Qyys/W52CKSMuT4P/vOPeec/9PJdO8gVFsc67dv ZihMWAuHwOzm2abmHhb8BG5KzIdsbz8omETV2/VfWNwDxJRistmW64EpD6FUBXVCNI8R fBLY5KKEuvcINBMf7G0c0gJMMzV65qEfrqtmqIbdbMt2/19TDKsCudslwlhULxOMlyJ6 IUGDm0r4qq4oMyN/xql4kvbFnAPSXuJhmpu3Tfkbzu3gjnKJ7vLozxk9dNrRjevWAOQQ 3QMsuAlf04FaaUoDWrUeLJT2mv7RWampXb+8okcehteTqmKyx+7t9boTLK7Q666OwmMO XGww== X-Gm-Message-State: AODbwcBh7caNXAEaTdkxhMlVq59to1jvyh7pRzKk68MHnZ0cUTdiInRg Q4HP5xQdb2qhzA== X-Received: by 10.84.233.141 with SMTP id l13mr26350239plk.298.1496818784141; Tue, 06 Jun 2017 23:59:44 -0700 (PDT) Received: from dtor-ws.mtv.corp.google.com ([172.22.152.34]) by smtp.gmail.com with ESMTPSA id r86sm1873184pfi.77.2017.06.06.23.59.43 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Tue, 06 Jun 2017 23:59:43 -0700 (PDT) From: Dmitry Torokhov To: Jiri Kosina , Benjamin Tissoires Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, Greg Kroah-Hartman Subject: [PATCH v2 4/8] HID: serialize hid_hw_open and hid_hw_close Date: Tue, 6 Jun 2017 23:59:34 -0700 Message-Id: <20170607065938.33332-5-dmitry.torokhov@gmail.com> X-Mailer: git-send-email 2.13.0.506.g27d5fe0cd-goog In-Reply-To: <20170607065938.33332-1-dmitry.torokhov@gmail.com> References: <20170607065938.33332-1-dmitry.torokhov@gmail.com> Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP The HID transport drivers either re-implement exactly the same logic (usbhid, i2c-hid) or forget to implement it (usbhid) which causes issues when the same device is accessed via multiple interfaces (for example input device through evdev and also hidraw). Let's muve the locking logic into HID core to make sure the serialized behavior is always enforced. Also let's uninline and move hid_hw_start() and hid_hw_stop() into hid-core as hid_hw_start() is somewhat large and do not believe we get any benefit from these two being inline. Signed-off-by: Dmitry Torokhov --- drivers/hid/hid-core.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/hid.h | 72 +++++----------------------------------- 2 files changed, 98 insertions(+), 63 deletions(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index ca173801d2be..f2a2cd522e46 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1750,6 +1750,94 @@ void hid_disconnect(struct hid_device *hdev) } EXPORT_SYMBOL_GPL(hid_disconnect); +/** + * hid_hw_start - start underlying HW + * @hdev: hid device + * @connect_mask: which outputs to connect, see HID_CONNECT_* + * + * Call this in probe function *after* hid_parse. This will setup HW + * buffers and start the device (if not defeirred to device open). + * hid_hw_stop must be called if this was successful. + */ +int hid_hw_start(struct hid_device *hdev, unsigned int connect_mask) +{ + int error; + + error = hdev->ll_driver->start(hdev); + if (error) + return error; + + if (connect_mask) { + error = hid_connect(hdev, connect_mask); + if (error) { + hdev->ll_driver->stop(hdev); + return error; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(hid_hw_start); + +/** + * hid_hw_stop - stop underlying HW + * @hdev: hid device + * + * This is usually called from remove function or from probe when something + * failed and hid_hw_start was called already. + */ +void hid_hw_stop(struct hid_device *hdev) +{ + hid_disconnect(hdev); + hdev->ll_driver->stop(hdev); +} +EXPORT_SYMBOL_GPL(hid_hw_stop); + +/** + * hid_hw_open - signal underlying HW to start delivering events + * @hdev: hid device + * + * Tell underlying HW to start delivering events from the device. + * This function should be called sometime after successful call + * to hid_hiw_start(). + */ +int hid_hw_open(struct hid_device *hdev) +{ + int ret; + + ret = mutex_lock_killable(&hdev->ll_open_lock); + if (ret) + return ret; + + if (!hdev->ll_open_count++) { + ret = hdev->ll_driver->open(hdev); + if (ret) + hdev->ll_open_count--; + } + + mutex_unlock(&hdev->ll_open_lock); + return ret; +} +EXPORT_SYMBOL_GPL(hid_hw_open); + +/** + * hid_hw_close - signal underlaying HW to stop delivering events + * + * @hdev: hid device + * + * This function indicates that we are not interested in the events + * from this device anymore. Delivery of events may or may not stop, + * depending on the number of users still outstanding. + */ +void hid_hw_close(struct hid_device *hdev) +{ + mutex_lock(&hdev->ll_open_lock); + if (!--hdev->ll_open_count) + hdev->ll_driver->close(hdev); + mutex_unlock(&hdev->ll_open_lock); +} +EXPORT_SYMBOL_GPL(hid_hw_close); + /* * A list of devices for which there is a specialized driver on HID bus. * @@ -2748,6 +2836,7 @@ struct hid_device *hid_allocate_device(void) spin_lock_init(&hdev->debug_list_lock); sema_init(&hdev->driver_lock, 1); sema_init(&hdev->driver_input_lock, 1); + mutex_init(&hdev->ll_open_lock); return hdev; } diff --git a/include/linux/hid.h b/include/linux/hid.h index 5be325d890d9..5501eb64dbc4 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -520,7 +521,10 @@ struct hid_device { /* device report descriptor */ struct semaphore driver_input_lock; /* protects the current driver */ struct device dev; /* device */ struct hid_driver *driver; + struct hid_ll_driver *ll_driver; + struct mutex ll_open_lock; + unsigned int ll_open_count; #ifdef CONFIG_HID_BATTERY_STRENGTH /* @@ -937,69 +941,11 @@ static inline int __must_check hid_parse(struct hid_device *hdev) return hid_open_report(hdev); } -/** - * hid_hw_start - start underlaying HW - * - * @hdev: hid device - * @connect_mask: which outputs to connect, see HID_CONNECT_* - * - * Call this in probe function *after* hid_parse. This will setup HW buffers - * and start the device (if not deffered to device open). hid_hw_stop must be - * called if this was successful. - */ -static inline int __must_check hid_hw_start(struct hid_device *hdev, - unsigned int connect_mask) -{ - int ret = hdev->ll_driver->start(hdev); - if (ret || !connect_mask) - return ret; - ret = hid_connect(hdev, connect_mask); - if (ret) - hdev->ll_driver->stop(hdev); - return ret; -} - -/** - * hid_hw_stop - stop underlaying HW - * - * @hdev: hid device - * - * This is usually called from remove function or from probe when something - * failed and hid_hw_start was called already. - */ -static inline void hid_hw_stop(struct hid_device *hdev) -{ - hid_disconnect(hdev); - hdev->ll_driver->stop(hdev); -} - -/** - * hid_hw_open - signal underlaying HW to start delivering events - * - * @hdev: hid device - * - * Tell underlying HW to start delivering events from the device. - * This function should be called sometime after successful call - * to hid_hiw_start(). - */ -static inline int __must_check hid_hw_open(struct hid_device *hdev) -{ - return hdev->ll_driver->open(hdev); -} - -/** - * hid_hw_close - signal underlaying HW to stop delivering events - * - * @hdev: hid device - * - * This function indicates that we are not interested in the events - * from this device anymore. Delivery of events may or may not stop, - * depending on the number of users still outstanding. - */ -static inline void hid_hw_close(struct hid_device *hdev) -{ - hdev->ll_driver->close(hdev); -} +int __must_check hid_hw_start(struct hid_device *hdev, + unsigned int connect_mask); +void hid_hw_stop(struct hid_device *hdev); +int __must_check hid_hw_open(struct hid_device *hdev); +void hid_hw_close(struct hid_device *hdev); /** * hid_hw_power - requests underlying HW to go into given power mode