Received: by 2002:a05:7412:3784:b0:e2:908c:2ebd with SMTP id jk4csp1446613rdb; Mon, 2 Oct 2023 09:42:29 -0700 (PDT) X-Google-Smtp-Source: AGHT+IEY9bzXUhG0hh+ldvO4gePhAfJJ/Bs8pzwAqgD2eRJgSmtIkGstV87IlrqtEOKhoWVhMUL6 X-Received: by 2002:a17:903:41d1:b0:1c5:a09e:170a with SMTP id u17-20020a17090341d100b001c5a09e170amr11552711ple.54.1696264949015; Mon, 02 Oct 2023 09:42:29 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1696264948; cv=none; d=google.com; s=arc-20160816; b=iC439wCEsdl4Sp4ALwQ2wDCzpOEMccqL0+40bK+d4OUggNbJJ1bSK70lnV2lPFLAEp De8khsvx5B/t0ZxMCJY0sFyOjNSmWAVbGxMP9QWJNqbsgO1PNbNrNRBW3iyEYFfwL+a3 in+liVA/f9F96w2HcUTMQLkmqJy0by93pmSsJmFi/QTbAGieoCL/2vbsbLv0wgrARBz6 QfJvcg4EAoQIiL8a97ifP+a1ChvUoM7T71RFFQGv2aSPm2w0LeMVBDhZuUQ+4ZpCSg2Y /7SbbEQNKyk+y+oAzhRgkTZDPTm1nPGRfNjqdH3lhXai6L5BC04PUKKE6iiLLReE7uaf XAQg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :message-id:date:subject:cc:to:from:dkim-signature; bh=wK3qtV2YMeTN2g6ztayss+PQ2Yh0SFczsNQEPCM3fc8=; fh=RHO8AkmR0iOeVFvHpRKuhuAYG8Z/b+/FIzCxzchGVJ0=; b=GpKW9oooHKnRzYLMqg4TRnRdLSiKlGbxxQKxuG2/hWnZI+quIxfJMLe2Mtc8Jlcc4R FPu2r1Iv3Z+WqaUVmIV4x41KK8oH0AHlfEKrUWlG4RslU7lXjf4ixiwxHj6FIy5T6TpT dMcdD3GsKvXuVlzgPSEkmtvAPq6o4NwedZMGdIoK1FBBVFG0H4rIzv2oSk9OW2CtmpE7 yQeytjgPecMefscDTyGlPYNK91uWphTNeGgb5Ng5d0Slhg2AZChQstgc24bcGL2WSkKW Jb3/CDA1YzNn3UbUr6A6hspiAoB3ybL/Uqxn9/gMjYBrlkhernz2eX+8JKO2NLUdPSpl 3OcA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@kernel.org header.s=k20201202 header.b=Kz4AOjuQ; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:5 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=kernel.org Return-Path: Received: from groat.vger.email (groat.vger.email. [2620:137:e000::3:5]) by mx.google.com with ESMTPS id j6-20020a170903024600b001bb0ff2b354si29451131plh.425.2023.10.02.09.42.28 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 02 Oct 2023 09:42:28 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:5 as permitted sender) client-ip=2620:137:e000::3:5; Authentication-Results: mx.google.com; dkim=pass header.i=@kernel.org header.s=k20201202 header.b=Kz4AOjuQ; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:5 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=kernel.org Received: from out1.vger.email (depot.vger.email [IPv6:2620:137:e000::3:0]) by groat.vger.email (Postfix) with ESMTP id B2C26803417B; Mon, 2 Oct 2023 09:01:28 -0700 (PDT) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.10 at groat.vger.email Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238368AbjJBQBK (ORCPT + 99 others); Mon, 2 Oct 2023 12:01:10 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51010 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238350AbjJBQBI (ORCPT ); Mon, 2 Oct 2023 12:01:08 -0400 Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 14F5FA4; Mon, 2 Oct 2023 09:01:05 -0700 (PDT) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 7BBCEC433C7; Mon, 2 Oct 2023 16:01:04 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1696262464; bh=dvY51J6UArWKifW/8zW62jttAxCmGzmMfpoJW5YMsVU=; h=From:To:Cc:Subject:Date:From; b=Kz4AOjuQyvDMr+rhVUX3LKQgjWMIlIt4TVnyoV2sLxpKI3Pyu0ucx6xHrF/2Vj+Ru nM7UEHWCNlrcl21iGE3h9GVpsDltiMWieW8xkx9XAozVJvrg7s//5jx3vgdd1Uu7dc UNBG9j/yjG4aWvPRWKzHAFLZTl5ki8dOMh/9CtpEj9EaNMiW8ugOzGcU+DzqJXjUXR wU1XFagJlukihe1px5O4Zfc2ZfELVetIQU2Xi7lebV93k59TrpmWEsBKE9TND/mdrU 9SWttF3fG4g8BU+lv6Mk0bd1Uixkem1kWKfP9tu73Qo0pP6xoCBw4+AccccVbKGWdy kT26sb+TRUWzw== Received: from johan by xi.lan with local (Exim 4.96) (envelope-from ) id 1qnLM8-0006PX-27; Mon, 02 Oct 2023 18:01:12 +0200 From: Johan Hovold To: Jiri Kosina , Benjamin Tissoires Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, Johan Hovold , Douglas Anderson , Maxime Ripard Subject: [PATCH v2] HID: i2c-hid: fix handling of unpopulated devices Date: Mon, 2 Oct 2023 17:58:57 +0200 Message-ID: <20231002155857.24584-1-johan+linaro@kernel.org> X-Mailer: git-send-email 2.41.0 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-1.2 required=5.0 tests=DKIMWL_WL_HIGH,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,MAILING_LIST_MULTI, SPF_HELO_NONE,SPF_PASS autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on groat.vger.email Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-Greylist: Sender passed SPF test, not delayed by milter-greylist-4.6.4 (groat.vger.email [0.0.0.0]); Mon, 02 Oct 2023 09:01:28 -0700 (PDT) A recent commit reordered probe so that the interrupt line is now requested before making sure that the device exists. This breaks machines like the Lenovo ThinkPad X13s which rely on the HID driver to probe second-source devices and only register the variant that is actually populated. Specifically, the interrupt line may now already be (temporarily) claimed when doing asynchronous probing of the touchpad: genirq: Flags mismatch irq 191. 00082008 (hid-over-i2c) vs. 00082008 (hid-over-i2c) i2c_hid_of 21-0015: Could not register for hid-over-i2c interrupt, irq = 191, ret = -16 i2c_hid_of: probe of 21-0015 failed with error -16 Fix this by restoring the old behaviour of first making sure the device exists before requesting the interrupt line. Note that something like this should probably be implemented also for "panel followers", whose actual probe is currently effectively deferred until the DRM panel is probed (e.g. by powering down the device after making sure it exists and only then register it as a follower). Fixes: 675cd877c952 ("HID: i2c-hid: Rearrange probe() to power things up later") Cc: Douglas Anderson Cc: Maxime Ripard Signed-off-by: Johan Hovold --- Changes in v2 - initialise ihid->is_panel_follower sooner to avoid repeated property lookups and so that it can be used consistently throughout the driver for code that differs for "panel followers" Link to v1: https://lore.kernel.org/lkml/20230918125851.310-1-johan+linaro@kernel.org/ drivers/hid/i2c-hid/i2c-hid-core.c | 144 ++++++++++++++++------------- 1 file changed, 81 insertions(+), 63 deletions(-) diff --git a/drivers/hid/i2c-hid/i2c-hid-core.c b/drivers/hid/i2c-hid/i2c-hid-core.c index 9601c0605fd9..2735cd585af0 100644 --- a/drivers/hid/i2c-hid/i2c-hid-core.c +++ b/drivers/hid/i2c-hid/i2c-hid-core.c @@ -998,45 +998,29 @@ static int i2c_hid_core_resume(struct i2c_hid *ihid) return hid_driver_reset_resume(hid); } -/** - * __do_i2c_hid_core_initial_power_up() - First time power up of the i2c-hid device. - * @ihid: The ihid object created during probe. - * - * This function is called at probe time. - * - * The initial power on is where we do some basic validation that the device - * exists, where we fetch the HID descriptor, and where we create the actual - * HID devices. - * - * Return: 0 or error code. +/* + * Check that the device exists and parse the HID descriptor. */ -static int __do_i2c_hid_core_initial_power_up(struct i2c_hid *ihid) +static int __i2c_hid_core_probe(struct i2c_hid *ihid) { struct i2c_client *client = ihid->client; struct hid_device *hid = ihid->hid; int ret; - ret = i2c_hid_core_power_up(ihid); - if (ret) - return ret; - /* Make sure there is something at this address */ ret = i2c_smbus_read_byte(client); if (ret < 0) { i2c_hid_dbg(ihid, "nothing at this address: %d\n", ret); - ret = -ENXIO; - goto err; + return -ENXIO; } ret = i2c_hid_fetch_hid_descriptor(ihid); if (ret < 0) { dev_err(&client->dev, "Failed to fetch the HID Descriptor\n"); - goto err; + return ret; } - enable_irq(client->irq); - hid->version = le16_to_cpu(ihid->hdesc.bcdVersion); hid->vendor = le16_to_cpu(ihid->hdesc.wVendorID); hid->product = le16_to_cpu(ihid->hdesc.wProductID); @@ -1050,17 +1034,49 @@ static int __do_i2c_hid_core_initial_power_up(struct i2c_hid *ihid) ihid->quirks = i2c_hid_lookup_quirk(hid->vendor, hid->product); + return 0; +} + +static int i2c_hid_core_register_hid(struct i2c_hid *ihid) +{ + struct i2c_client *client = ihid->client; + struct hid_device *hid = ihid->hid; + int ret; + + enable_irq(client->irq); + ret = hid_add_device(hid); if (ret) { if (ret != -ENODEV) hid_err(client, "can't add hid device: %d\n", ret); - goto err; + disable_irq(client->irq); + return ret; } return 0; +} + +static int i2c_hid_core_probe_panel_follower(struct i2c_hid *ihid) +{ + int ret; + + ret = i2c_hid_core_power_up(ihid); + if (ret) + return ret; -err: + ret = __i2c_hid_core_probe(ihid); + if (ret) + goto err_power_down; + + ret = i2c_hid_core_register_hid(ihid); + if (ret) + goto err_power_down; + + return 0; + +err_power_down: i2c_hid_core_power_down(ihid); + return ret; } @@ -1077,7 +1093,7 @@ static void ihid_core_panel_prepare_work(struct work_struct *work) * steps. */ if (!hid->version) - ret = __do_i2c_hid_core_initial_power_up(ihid); + ret = i2c_hid_core_probe_panel_follower(ihid); else ret = i2c_hid_core_resume(ihid); @@ -1136,7 +1152,6 @@ static int i2c_hid_core_register_panel_follower(struct i2c_hid *ihid) struct device *dev = &ihid->client->dev; int ret; - ihid->is_panel_follower = true; ihid->panel_follower.funcs = &i2c_hid_core_panel_follower_funcs; /* @@ -1156,30 +1171,6 @@ static int i2c_hid_core_register_panel_follower(struct i2c_hid *ihid) return 0; } -static int i2c_hid_core_initial_power_up(struct i2c_hid *ihid) -{ - /* - * If we're a panel follower, we'll register and do our initial power - * up when the panel turns on; otherwise we do it right away. - */ - if (drm_is_panel_follower(&ihid->client->dev)) - return i2c_hid_core_register_panel_follower(ihid); - else - return __do_i2c_hid_core_initial_power_up(ihid); -} - -static void i2c_hid_core_final_power_down(struct i2c_hid *ihid) -{ - /* - * If we're a follower, the act of unfollowing will cause us to be - * powered down. Otherwise we need to manually do it. - */ - if (ihid->is_panel_follower) - drm_panel_remove_follower(&ihid->panel_follower); - else - i2c_hid_core_suspend(ihid, true); -} - int i2c_hid_core_probe(struct i2c_client *client, struct i2chid_ops *ops, u16 hid_descriptor_address, u32 quirks) { @@ -1211,6 +1202,7 @@ int i2c_hid_core_probe(struct i2c_client *client, struct i2chid_ops *ops, ihid->ops = ops; ihid->client = client; ihid->wHIDDescRegister = cpu_to_le16(hid_descriptor_address); + ihid->is_panel_follower = drm_is_panel_follower(&client->dev); init_waitqueue_head(&ihid->wait); mutex_init(&ihid->reset_lock); @@ -1224,14 +1216,10 @@ int i2c_hid_core_probe(struct i2c_client *client, struct i2chid_ops *ops, return ret; device_enable_async_suspend(&client->dev); - ret = i2c_hid_init_irq(client); - if (ret < 0) - goto err_buffers_allocated; - hid = hid_allocate_device(); if (IS_ERR(hid)) { ret = PTR_ERR(hid); - goto err_irq; + goto err_free_buffers; } ihid->hid = hid; @@ -1242,19 +1230,42 @@ int i2c_hid_core_probe(struct i2c_client *client, struct i2chid_ops *ops, hid->bus = BUS_I2C; hid->initial_quirks = quirks; - ret = i2c_hid_core_initial_power_up(ihid); + /* Power on and probe unless device is a panel follower. */ + if (!ihid->is_panel_follower) { + ret = i2c_hid_core_power_up(ihid); + if (ret < 0) + goto err_destroy_device; + + ret = __i2c_hid_core_probe(ihid); + if (ret < 0) + goto err_power_down; + } + + ret = i2c_hid_init_irq(client); + if (ret < 0) + goto err_power_down; + + /* + * If we're a panel follower, we'll register when the panel turns on; + * otherwise we do it right away. + */ + if (ihid->is_panel_follower) + ret = i2c_hid_core_register_panel_follower(ihid); + else + ret = i2c_hid_core_register_hid(ihid); if (ret) - goto err_mem_free; + goto err_free_irq; return 0; -err_mem_free: - hid_destroy_device(hid); - -err_irq: +err_free_irq: free_irq(client->irq, ihid); - -err_buffers_allocated: +err_power_down: + if (!ihid->is_panel_follower) + i2c_hid_core_power_down(ihid); +err_destroy_device: + hid_destroy_device(hid); +err_free_buffers: i2c_hid_free_buffers(ihid); return ret; @@ -1266,7 +1277,14 @@ void i2c_hid_core_remove(struct i2c_client *client) struct i2c_hid *ihid = i2c_get_clientdata(client); struct hid_device *hid; - i2c_hid_core_final_power_down(ihid); + /* + * If we're a follower, the act of unfollowing will cause us to be + * powered down. Otherwise we need to manually do it. + */ + if (ihid->is_panel_follower) + drm_panel_remove_follower(&ihid->panel_follower); + else + i2c_hid_core_suspend(ihid, true); hid = ihid->hid; hid_destroy_device(hid); -- 2.41.0