From patchwork Mon Jun 3 14:43:57 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andreas Kemnade X-Patchwork-Id: 801285 Received: from mail2.andi.de1.cc (vmd64148.contaboserver.net [161.97.139.27]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F1E1E82D8E; Mon, 3 Jun 2024 14:44:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=161.97.139.27 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1717425871; cv=none; b=TLACjSJdC2q5O5mCOKEKjX2cVbhx78rsLvalJZcDILo30G2AYk6d898ZVaB+OY9ivyxxjMr63uTUEBDOl/5c/DRxQ0cpPenhCZ8/LJTvHaYfqVSMpc7m9whT5UDWmJp+YtVoYthpGTuqFyqyMeckdHGT7maTez1eKKsKgE+jBtU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1717425871; c=relaxed/simple; bh=6kMGZvbbwAVpUY7o/3Mo1hVEWC+DsN8qKwLMZzsvR7s=; h=From:To:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=jjQd+8QPHnf5VxHUcJY3skyebWrS0Y+HHOPy6x63K88L+cs3cjnmDk0DTHjf+ofX5xPscFEnmQEvVlUREmI6NQxbmSWWRgh9lUpy2ABk6gt9giM5gEsAmtQZrP/9ku6s6JzU4E0TT6B6SQ5z8fu9MKNMNm73OaI1HZ6nGVNJ01M= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=kemnade.info; spf=pass smtp.mailfrom=kemnade.info; dkim=pass (2048-bit key) header.d=kemnade.info header.i=@kemnade.info header.b=mQigtz+E; arc=none smtp.client-ip=161.97.139.27 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=kemnade.info Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=kemnade.info Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kemnade.info header.i=@kemnade.info header.b="mQigtz+E" Received: from mail.andi.de1.cc ([2a02:c205:3004:2154::1]) by mail2.andi.de1.cc with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1sE8v6-008hAe-2w; Mon, 03 Jun 2024 16:44:22 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=kemnade.info; s=20220719; h=Content-Transfer-Encoding:MIME-Version: References:In-Reply-To:Message-Id:Date:Subject:To:From:Sender:Reply-To:Cc: Content-Type:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help: List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive; bh=bMF1CyWf3RbQIO/q5KxisncBbMfKZqOgAKsNghJdEpk=; b=mQigtz+Ea4BY1ZoY399w8Dqs9i BwDrTkYfBhqIIt2dN8f3yD7SSH27sC+lzGBOR9WzQG4W0Odq5eNjJr+G4WFhAmC6dJpQqIqwyccdc h9G54vhXpqtlmJdL8xsLKe8DD/3aUegBBcYpZPnlHG+7imipme3/lO7ZxxYJvUKOKZAF3NdBFStv4 nTRk6GFNv+yzj7bHipc05RXZ873IwH/p2xAMeBytJzDE1e9Ex2D7IF12Veg8pdk8VWcG6UYQeTtpL S9x+mDzlWXVtRTH75KMDKnlviNRHiUkwi3PZlKmQGjlzJQ+pU42BlZFHdt9EqTREdT5UewhauS3Rg DrIRAW7g==; Received: from p200300c20737c2001a3da2fffebfd33a.dip0.t-ipconnect.de ([2003:c2:737:c200:1a3d:a2ff:febf:d33a] helo=aktux) by mail.andi.de1.cc with esmtpsa (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1sE8v3-002Uea-1s; Mon, 03 Jun 2024 16:44:18 +0200 Received: from andi by aktux with local (Exim 4.96) (envelope-from ) id 1sE8v4-009DAn-1x; Mon, 03 Jun 2024 16:44:18 +0200 From: Andreas Kemnade To: marcel@holtmann.org, luiz.dentz@gmail.com, johan@kernel.org, gregkh@linuxfoundation.org, jirislaby@kernel.org, andreas@kemnade.info, pmenzel@molgen.mpg.de, linux-kernel@vger.kernel.org, linux-bluetooth@vger.kernel.org, Adam Ford , Tony Lindgren , tomi.valkeinen@ideasonboard.com, =?utf-8?q?P=C3=A9ter_Ujfalusi?= , robh@kernel.org, hns@goldelico.com Subject: [PATCH v3 1/4] gnss: Add AI2 protocol used by some TI combo chips. Date: Mon, 3 Jun 2024 16:43:57 +0200 Message-Id: <20240603144400.2195564-2-andreas@kemnade.info> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240603144400.2195564-1-andreas@kemnade.info> References: <20240603144400.2195564-1-andreas@kemnade.info> Precedence: bulk X-Mailing-List: linux-bluetooth@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Texas Instruments uses something called Air Independent Interface (AI2) for their WLAN/BT/GPS combo chips. No public documentation is available, but allow that protocol to be specified. Signed-off-by: Andreas Kemnade Reviewed-by: Paul Menzel --- drivers/gnss/core.c | 1 + include/linux/gnss.h | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/gnss/core.c b/drivers/gnss/core.c index 48f2ee0f78c4d..cac9f45aec4b2 100644 --- a/drivers/gnss/core.c +++ b/drivers/gnss/core.c @@ -335,6 +335,7 @@ static const char * const gnss_type_names[GNSS_TYPE_COUNT] = { [GNSS_TYPE_SIRF] = "SiRF", [GNSS_TYPE_UBX] = "UBX", [GNSS_TYPE_MTK] = "MTK", + [GNSS_TYPE_AI2] = "AI2", }; static const char *gnss_type_name(const struct gnss_device *gdev) diff --git a/include/linux/gnss.h b/include/linux/gnss.h index 36968a0f33e8d..16b565dab83ea 100644 --- a/include/linux/gnss.h +++ b/include/linux/gnss.h @@ -23,6 +23,7 @@ enum gnss_type { GNSS_TYPE_SIRF, GNSS_TYPE_UBX, GNSS_TYPE_MTK, + GNSS_TYPE_AI2, GNSS_TYPE_COUNT }; From patchwork Mon Jun 3 14:43:58 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andreas Kemnade X-Patchwork-Id: 801748 Received: from mail2.andi.de1.cc (vmd64148.contaboserver.net [161.97.139.27]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F1EA612F5A0; Mon, 3 Jun 2024 14:44:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=161.97.139.27 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1717425871; cv=none; b=U3Qtgdfw8z0rcF1HSxZ/v5oketHFM9LrKv2J1NOI7yn+28+fOyOXXBpG+21mRuUApSM9ipowDntlhojYn8GxsXjK5lpnXpyPl2aHDO7qwCe/hf6lvKi2IWBXDFEN8OwTzFTsQ1IbtpFVfbg7aBMs30BmIrGmBW7zZeumLojGVuU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1717425871; c=relaxed/simple; bh=YCFJH9i8CYXUpt91sYS6Dq7QkAvQFkMqR1g//KHWhBk=; h=From:To:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=ET/Y3fL9cnuhZgHeeo1AD5M/VXafXXXlshsnTQHz0EmtHJHNwNaOLvibjbw2vYqfL4Up1XeK9E4MyUmlF7yS+D3HmLhFbgS/NzLMBxxbaPLXv9hZ1M+LaVsEfZ3POq7ifoYoUbYs/kXhuRxcKeAVP4vaoqfpSaxByaYhmNvuVd0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=kemnade.info; spf=pass smtp.mailfrom=kemnade.info; dkim=pass (2048-bit key) header.d=kemnade.info header.i=@kemnade.info header.b=j8BbcaRf; arc=none smtp.client-ip=161.97.139.27 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=kemnade.info Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=kemnade.info Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kemnade.info header.i=@kemnade.info header.b="j8BbcaRf" Received: from mail.andi.de1.cc ([2a02:c205:3004:2154::1]) by mail2.andi.de1.cc with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1sE8v6-008hAg-2w; Mon, 03 Jun 2024 16:44:22 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=kemnade.info; s=20220719; h=Content-Transfer-Encoding:MIME-Version: References:In-Reply-To:Message-Id:Date:Subject:To:From:Sender:Reply-To:Cc: Content-Type:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help: List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive; bh=tmLRwCw5fvmkHYewvTmSzsnpuIGD6xKMMA1I4DUGgaI=; b=j8BbcaRfNS6Vdc/k6w/nI8VkuC 0eec8EBLSp3jKXLaKdO74cuXgv6sJ35uh6E7DBtiw9rSZzl/w1o4/ScXen/Kmnti9PglxBLO7w44t D0aVM6F3Pf4cabos37gLovHDgt9EvxlvSWnpyPJiSO93nC+qZO9edKhdoici7c0A81TAxjZIRsJM4 HzEchyNJVLcr8+k46Yjo6gt16rvLTfhijw0qQ3uHt96IFSwejtO645H3WWHo2GK9b4QhJvv0vIiL1 gAEPoilLNzlVFAkeqT5OTKN/wx2Bx8jkxry4Jek3IqtZDqciwkiTyza33Hk3FG8xrtE+0teTn489b X9rOMx9w==; Received: from p200300c20737c2001a3da2fffebfd33a.dip0.t-ipconnect.de ([2003:c2:737:c200:1a3d:a2ff:febf:d33a] helo=aktux) by mail.andi.de1.cc with esmtpsa (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1sE8v5-002Uep-0m; Mon, 03 Jun 2024 16:44:20 +0200 Received: from andi by aktux with local (Exim 4.96) (envelope-from ) id 1sE8v6-009DAs-0n; Mon, 03 Jun 2024 16:44:20 +0200 From: Andreas Kemnade To: marcel@holtmann.org, luiz.dentz@gmail.com, johan@kernel.org, gregkh@linuxfoundation.org, jirislaby@kernel.org, andreas@kemnade.info, pmenzel@molgen.mpg.de, linux-kernel@vger.kernel.org, linux-bluetooth@vger.kernel.org, Adam Ford , Tony Lindgren , tomi.valkeinen@ideasonboard.com, =?utf-8?q?P=C3=A9ter_Ujfalusi?= , robh@kernel.org, hns@goldelico.com Subject: [PATCH v3 2/4] Bluetooth: ti-st: Add GNSS subdevice for TI Wilink chips Date: Mon, 3 Jun 2024 16:43:58 +0200 Message-Id: <20240603144400.2195564-3-andreas@kemnade.info> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240603144400.2195564-1-andreas@kemnade.info> References: <20240603144400.2195564-1-andreas@kemnade.info> Precedence: bulk X-Mailing-List: linux-bluetooth@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Some of these chips have GNSS support. GNSS support is available through channel 9 whilst FM is through channel 8. Add a platform subdevice for GNSS so that a driver for that functionality can be build. To avoid having useless GNSS devices, do it only when the devicetree node name contains gnss. Signed-off-by: Andreas Kemnade Reviewed-by: Paul Menzel --- drivers/bluetooth/hci_ll.c | 81 ++++++++++++++++++++++++++++++++++++ include/linux/ti_wilink_st.h | 8 ++++ 2 files changed, 89 insertions(+) diff --git a/drivers/bluetooth/hci_ll.c b/drivers/bluetooth/hci_ll.c index 4a0b5c3160c2b..09e5a4dbd2f8c 100644 --- a/drivers/bluetooth/hci_ll.c +++ b/drivers/bluetooth/hci_ll.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -68,6 +69,9 @@ struct ll_device { struct gpio_desc *enable_gpio; struct clk *ext_clk; bdaddr_t bdaddr; + + void (*gnss_recv_func)(struct device *dev, struct sk_buff *skb); + struct platform_device *gnssdev; }; struct ll_struct { @@ -78,6 +82,8 @@ struct ll_struct { struct sk_buff_head tx_wait_q; /* HCILL wait queue */ }; +static int ll_gnss_register(struct ll_device *lldev); +static int ll_gnss_recv_frame(struct hci_dev *hdev, struct sk_buff *skb); /* * Builds and sends an HCILL command packet. * These are very simple packets with only 1 cmd byte @@ -411,6 +417,13 @@ static int ll_recv_frame(struct hci_dev *hdev, struct sk_buff *skb) .lsize = 0, \ .maxlen = 0 +#define LL_RECV_GNSS \ + .type = 9, \ + .hlen = 3, \ + .loff = 1, \ + .lsize = 2 + + static const struct h4_recv_pkt ll_recv_pkts[] = { { H4_RECV_ACL, .recv = hci_recv_frame }, { H4_RECV_SCO, .recv = hci_recv_frame }, @@ -419,6 +432,7 @@ static const struct h4_recv_pkt ll_recv_pkts[] = { { LL_RECV_SLEEP_ACK, .recv = ll_recv_frame }, { LL_RECV_WAKE_IND, .recv = ll_recv_frame }, { LL_RECV_WAKE_ACK, .recv = ll_recv_frame }, + { LL_RECV_GNSS, .recv = ll_gnss_recv_frame }, }; /* Recv data */ @@ -677,9 +691,69 @@ static int ll_setup(struct hci_uart *hu) } } + if (strstr(of_node_full_name(serdev->dev.of_node), "gnss")) + ll_gnss_register(lldev); + + return 0; +} + +struct hci_dev *st_get_hci(struct device *dev) +{ + struct ll_device *lldev = dev_get_drvdata(dev); + + return lldev->hu.hdev; +} +EXPORT_SYMBOL(st_get_hci); + +void st_set_gnss_recv_func(struct device *dev, + void (*recv_frame)(struct device *, struct sk_buff *)) +{ + struct ll_device *lldev = dev_get_drvdata(dev); + + lldev->gnss_recv_func = recv_frame; +} +EXPORT_SYMBOL(st_set_gnss_recv_func); + +static int ll_gnss_recv_frame(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_uart *hu = hci_get_drvdata(hdev); + struct ll_device *lldev = container_of(hu, struct ll_device, hu); + + if (!lldev->gnssdev) + return 0; + + if (lldev->gnss_recv_func) { + lldev->gnss_recv_func(&lldev->gnssdev->dev, skb); + return 0; + } + kfree_skb(skb); + return 0; } +static int ll_gnss_register(struct ll_device *lldev) +{ + struct platform_device *pdev; + int ret; + + pdev = platform_device_alloc("ti-ai2-gnss", PLATFORM_DEVID_AUTO); + if (!pdev) + return -ENOMEM; + + pdev->dev.parent = &lldev->serdev->dev; + lldev->gnssdev = pdev; + ret = platform_device_add(pdev); + if (ret) + goto err; + + return 0; + +err: + lldev->gnssdev = NULL; + platform_device_put(pdev); + return ret; +} + static const struct hci_uart_proto llp; static int hci_ti_probe(struct serdev_device *serdev) @@ -757,12 +831,19 @@ static int hci_ti_probe(struct serdev_device *serdev) } return hci_uart_register_device(hu, &llp); + + + return 0; } + static void hci_ti_remove(struct serdev_device *serdev) { struct ll_device *lldev = serdev_device_get_drvdata(serdev); + if (lldev->gnssdev) + platform_device_unregister(lldev->gnssdev); + hci_uart_unregister_device(&lldev->hu); } diff --git a/include/linux/ti_wilink_st.h b/include/linux/ti_wilink_st.h index 10642d4844f0c..eccc2db004069 100644 --- a/include/linux/ti_wilink_st.h +++ b/include/linux/ti_wilink_st.h @@ -381,6 +381,14 @@ unsigned long st_ll_getstate(struct st_data_s *); unsigned long st_ll_sleep_state(struct st_data_s *, unsigned char); void st_ll_wakeup(struct st_data_s *); +/** + * various funcs used to interact between FM, GPS and BT + */ +struct hci_dev *st_get_hci(struct device *dev); +void st_set_gnss_recv_func(struct device *dev, + void (*recv_frame)(struct device *, struct sk_buff *)); + + /* * header information used by st_core.c for FM and GPS * packet parsing, the bluetooth headers are already available From patchwork Mon Jun 3 14:43:59 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andreas Kemnade X-Patchwork-Id: 801284 Received: from mail2.andi.de1.cc (vmd64148.contaboserver.net [161.97.139.27]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B51DC1311A8; Mon, 3 Jun 2024 14:44:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=161.97.139.27 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1717425875; cv=none; b=u08irMra2KH5cDTKwzEoaOk5rh4lut2qnb9kEGlPzPUu75kATwXvOqJGS6vOIiI9vdO2F9ypDb5AJCzT4uQhgaWEl780+JXtdvdxbyjkIumxNM8vUpdc5ucyeCXSvHtcxBQ8KgTnlHglk3tBlZsNDqf767mwxqCUuhK59zdZ3M0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1717425875; c=relaxed/simple; bh=JqkpiY/+hZD2WCNVH3McGoAN9RlplThIwX7uduQlb3k=; h=From:To:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=a2TkOPv++La18QTZQWPdlI1xMPT6mjT0fJeQJBls6AcDT+sKia/v5oynxVhHVDoLkJxEB1s7DZs/pN1tLsOIoMXOjgpJl8a+CwV/20ydLho2kNsZvz7YZ3fYG3NEzgZUTxpzFe1fKleY5sN7WN6kf+ChBFpnTku2y4IYH91EcRc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=kemnade.info; spf=pass smtp.mailfrom=kemnade.info; dkim=pass (2048-bit key) header.d=kemnade.info header.i=@kemnade.info header.b=MS5EINeh; arc=none smtp.client-ip=161.97.139.27 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=kemnade.info Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=kemnade.info Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kemnade.info header.i=@kemnade.info header.b="MS5EINeh" Received: from mail.andi.de1.cc ([2a02:c205:3004:2154::1]) by mail2.andi.de1.cc with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1sE8vA-008hAs-1Q; Mon, 03 Jun 2024 16:44:25 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=kemnade.info; s=20220719; h=Content-Transfer-Encoding:MIME-Version: References:In-Reply-To:Message-Id:Date:Subject:To:From:Sender:Reply-To:Cc: Content-Type:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help: List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive; bh=drKHUgwKkW2PbyIoNGASeFX7RLuVgwpvtctgYrIf9qo=; b=MS5EINehO8YEtY8F87HDRvWXR0 0XZcmZxSX1DTPUc5UT8M5At853EV28npV7+V3Gac4+oaSRZXN3JQXzjegKCk8czvD/3nr/PPu99w2 RWolAj6YftO8sIV75X5LVrwtcKrecI+SuIh8AD8BkuZkBo/v11elD0LS0nbmpvBd/Bu/2h8q4jdgH R9wDWMP3p06JxhYzJOnYB8uvXExCuM/U9EtAaa/vP5+VTrIzmCuPCZepz8PDla1o2bfyBRCTgniyk FtUBm8pGs3ga/s8WXsuCtPJXuXZpyBFYGo9PpcK2jjktS9SMto8n2lOwWnZ3nnVCMqNmjrAmqeaRE CRP1e4Qw==; Received: from p200300c20737c2001a3da2fffebfd33a.dip0.t-ipconnect.de ([2003:c2:737:c200:1a3d:a2ff:febf:d33a] helo=aktux) by mail.andi.de1.cc with esmtpsa (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1sE8v8-002UfH-1z; Mon, 03 Jun 2024 16:44:23 +0200 Received: from andi by aktux with local (Exim 4.96) (envelope-from ) id 1sE8v9-009DAx-1y; Mon, 03 Jun 2024 16:44:23 +0200 From: Andreas Kemnade To: marcel@holtmann.org, luiz.dentz@gmail.com, johan@kernel.org, gregkh@linuxfoundation.org, jirislaby@kernel.org, andreas@kemnade.info, pmenzel@molgen.mpg.de, linux-kernel@vger.kernel.org, linux-bluetooth@vger.kernel.org, Adam Ford , Tony Lindgren , tomi.valkeinen@ideasonboard.com, =?utf-8?q?P=C3=A9ter_Ujfalusi?= , robh@kernel.org, hns@goldelico.com Subject: [PATCH v3 3/4] gnss: Add driver for AI2 protocol Date: Mon, 3 Jun 2024 16:43:59 +0200 Message-Id: <20240603144400.2195564-4-andreas@kemnade.info> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240603144400.2195564-1-andreas@kemnade.info> References: <20240603144400.2195564-1-andreas@kemnade.info> Precedence: bulk X-Mailing-List: linux-bluetooth@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Add a driver for the Air Independent Interface protocol used by some TI Wilink combo chips. Per default, send out just NMEA to userspace and turn on/off things at open()/close() but keep the door open for any sophisticated development regarding the AI2 protocol by having a kernel parameter to turn it into raw mode (ai2raw) resembling /dev/tigps provided by some TI vendor kernels. The fork used by the BT200 is at: http://epsonservice.goepson.com/downloads/VI-APS/BT200_kernel.tgz Signed-off-by: Andreas Kemnade Acked-by: Paul Menzel --- drivers/gnss/Kconfig | 13 ++ drivers/gnss/Makefile | 3 + drivers/gnss/ai2.c | 527 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 543 insertions(+) create mode 100644 drivers/gnss/ai2.c diff --git a/drivers/gnss/Kconfig b/drivers/gnss/Kconfig index d7fe265c28696..95fdab6e7ae94 100644 --- a/drivers/gnss/Kconfig +++ b/drivers/gnss/Kconfig @@ -65,4 +65,17 @@ config GNSS_USB If unsure, say N. +config GNSS_AI2 + tristate "TI AI2 procotol support" + depends on BT_HCIUART_LL + help + Say Y here if you have a Texas Instruments Wilink combo chip + containing among other things a GNSS receiver speaking the + Air Independent Interface (AI2) protocol. + + To compile this driver as a module, choose M here: the module will + be called gnss-ai2. + + If unsure, say N. + endif # GNSS diff --git a/drivers/gnss/Makefile b/drivers/gnss/Makefile index bb2cbada34359..bf6fefcb2e823 100644 --- a/drivers/gnss/Makefile +++ b/drivers/gnss/Makefile @@ -20,3 +20,6 @@ gnss-ubx-y := ubx.o obj-$(CONFIG_GNSS_USB) += gnss-usb.o gnss-usb-y := usb.o + +obj-$(CONFIG_GNSS_AI2) += gnss-ai2.o +gnss-ai2-y := ai2.o diff --git a/drivers/gnss/ai2.c b/drivers/gnss/ai2.c new file mode 100644 index 0000000000000..0cc21b64b7c3f --- /dev/null +++ b/drivers/gnss/ai2.c @@ -0,0 +1,527 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Texas Instruments AI2 (Air independent interface) protocol device driver + * Used for some TI WLAN/Bluetooth/GNSS combo chips. + * + * Copyright (C) 2024 Andreas Kemnade + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Channel-9 details for GPS */ +#define GPS_CH9_PKT_NUMBER 0x9 +#define GPS_CH9_OP_WRITE 0x1 +#define GPS_CH9_OP_READ 0x2 +#define GPS_CH9_OP_COMPLETED_EVT 0x3 + +/* arbitarily chosen, should fit everything seen in the past */ +#define MAX_AI2_FRAME_SIZE 2048 + +#define AI2_ESCAPE 0x10 /* if sent as data, it is doubled */ +#define AI2_END_MARKER 0x3 +#define AI2_ACK 0x2 + +/* reports */ +#define AI2_REPORT_NMEA 0xd3 + +#define NMEA_HEADER_LEN 4 + +/* commands */ +#define AI2_CMD_RECEIVER_STATE 2 + +#define RECEIVER_STATE_OFF 1 +#define RECEIVER_STATE_IDLE 2 +#define RECEIVER_STATE_ON 3 + +#define AI2_CMD_CONFIG_NMEA 0xe5 +#define NMEA_MASK_GGA (1 << 0) +#define NMEA_MASK_GLL (1 << 1) +#define NMEA_MASK_GSA (1 << 2) +#define NMEA_MASK_GSV (1 << 3) +#define NMEA_MASK_RMC (1 << 4) +#define NMEA_MASK_VTG (1 << 5) + +#define NMEA_MASK_ALL (NMEA_MASK_GGA | \ + NMEA_MASK_GLL | \ + NMEA_MASK_GSA | \ + NMEA_MASK_GSV | \ + NMEA_MASK_RMC | \ + NMEA_MASK_VTG) + + +static bool ai2raw; + +struct ai2_device { + struct mutex gdev_mutex; + bool gdev_open; + struct gnss_device *gdev; + struct device *dev; + struct sk_buff *recv_skb; + bool recv_esc; +}; + +static struct sk_buff *ai2_skb_alloc(unsigned int len, gfp_t how) +{ + struct sk_buff *skb; + + skb = bt_skb_alloc(len + sizeof(struct gps_event_hdr), how); + if (skb) + skb_reserve(skb, sizeof(struct gps_event_hdr)); + + return skb; +} + +static int ai2_send_frame(struct ai2_device *ai2dev, + struct sk_buff *skb) +{ + int len; + struct gps_event_hdr *gnssdrv_hdr; + struct hci_dev *hdev; + + if (skb->len >= U16_MAX) + return -EINVAL; + + /* + * note: fragmentation at this point not handled yet + * not needed for simple config commands + */ + len = skb->len; + gnssdrv_hdr = skb_push(skb, sizeof(struct gps_event_hdr)); + gnssdrv_hdr->opcode = GPS_CH9_OP_WRITE; + gnssdrv_hdr->plen = __cpu_to_le16(len); + + hci_skb_pkt_type(skb) = GPS_CH9_PKT_NUMBER; + hdev = st_get_hci(ai2dev->dev->parent); + return hdev->send(hdev, skb); +} + +static void ai2_put_escaped(struct sk_buff *skb, u8 d) +{ + skb_put_u8(skb, d); + if (d == 0x10) + skb_put_u8(skb, d); +} + +static struct sk_buff *ai2_compose_frame(bool request_ack, + u8 cmd, + const u8 *data, + int len) +{ + u16 sum; + int i; + /* duplicate the length to have space for worst case escaping */ + struct sk_buff *skb = ai2_skb_alloc(2 + len * 2 + 2 + 2, GFP_KERNEL); + + skb_put_u8(skb, AI2_ESCAPE); + skb_put_u8(skb, request_ack ? 1 : 0); + + sum = AI2_ESCAPE; + if (request_ack) + sum++; + + ai2_put_escaped(skb, cmd); + sum += cmd; + + ai2_put_escaped(skb, len & 0xff); + sum += len & 0xff; + + ai2_put_escaped(skb, len >> 8); + sum += len >> 8; + + for (i = 0; i < len; i++) { + sum += data[i]; + ai2_put_escaped(skb, data[i]); + } + + ai2_put_escaped(skb, sum & 0xFF); + ai2_put_escaped(skb, sum >> 8); + skb_put_u8(skb, AI2_ESCAPE); + skb_put_u8(skb, AI2_END_MARKER); + + return skb; +} + +static int ai2_set_receiver_state(struct ai2_device *ai2dev, + uint8_t state) +{ + struct sk_buff *skb = ai2_compose_frame(true, AI2_CMD_RECEIVER_STATE, + &state, 1); + if (!skb) + return -ENOMEM; + + return ai2_send_frame(ai2dev, skb); +} + +static int ai2_config_nmea_reports(struct ai2_device *ai2dev, + uint8_t mask) +{ + u8 buf[4] = {0}; + struct sk_buff *skb; + + buf[0] = mask; + skb = ai2_compose_frame(true, AI2_CMD_CONFIG_NMEA, + buf, sizeof(buf)); + if (!skb) + return -ENOMEM; + + return ai2_send_frame(ai2dev, skb); +} + +/* + * Unknown commands, give some version information, must be sent + * once, not sure what undoes them besides resetting the whole + * bluetooth part, but no signs of significant things being still + * turned on without undoing this. + */ +static int gnss_ai2_init(struct ai2_device *ai2dev) +{ + int ret; + u8 d = 0x01; + struct sk_buff *skb = ai2_compose_frame(true, 0xf5, &d, 1); + + if (!skb) + return -ENOMEM; + + ret = ai2_send_frame(ai2dev, skb); + if (ret) + return ret; + + msleep(200); /* seen some 60ms response time here, so wait a bit */ + d = 5; + skb = ai2_compose_frame(true, 0xf1, &d, 1); + if (!skb) + return -ENOMEM; + + return ai2_send_frame(ai2dev, skb); +} + +static int gnss_ai2_open(struct gnss_device *gdev) +{ + struct ai2_device *ai2dev = gnss_get_drvdata(gdev); + int ret; + + mutex_lock(&ai2dev->gdev_mutex); + ai2dev->gdev_open = true; + mutex_unlock(&ai2dev->gdev_mutex); + if (ai2raw) + return 0; + + ret = gnss_ai2_init(ai2dev); + if (ret) + goto err; + + /* TODO: find out on what kind of ack we should wait */ + msleep(50); + ret = ai2_set_receiver_state(ai2dev, RECEIVER_STATE_IDLE); + if (ret) + goto err; + + msleep(100); + ret = ai2_config_nmea_reports(ai2dev, NMEA_MASK_ALL); + if (ret) + goto err; + + msleep(50); + ret = ai2_set_receiver_state(ai2dev, RECEIVER_STATE_ON); + if (ret) + goto err; + + msleep(50); + + return 0; +err: + mutex_lock(&ai2dev->gdev_mutex); + ai2dev->gdev_open = false; + if (ai2dev->recv_skb) + kfree_skb(ai2dev->recv_skb); + + ai2dev->recv_skb = NULL; + mutex_unlock(&ai2dev->gdev_mutex); + return ret; +} + +static void gnss_ai2_close(struct gnss_device *gdev) +{ + struct ai2_device *ai2dev = gnss_get_drvdata(gdev); + + /* TODO: find out on what kind of ack we should wait */ + if (!ai2raw) { + msleep(50); + ai2_set_receiver_state(ai2dev, RECEIVER_STATE_IDLE); + msleep(50); + ai2_set_receiver_state(ai2dev, RECEIVER_STATE_OFF); + msleep(200); /* seen some longer response time here, so wait */ + } + + mutex_lock(&ai2dev->gdev_mutex); + ai2dev->gdev_open = false; + if (ai2dev->recv_skb) + kfree_skb(ai2dev->recv_skb); + + ai2dev->recv_skb = NULL; + mutex_unlock(&ai2dev->gdev_mutex); +} + + +static int gnss_ai2_write_raw(struct gnss_device *gdev, + const unsigned char *buf, size_t count) +{ + struct ai2_device *ai2dev = gnss_get_drvdata(gdev); + int err = 0; + struct sk_buff *skb = NULL; + + if (!ai2raw) + return -EPERM; + + /* allocate packet */ + skb = ai2_skb_alloc(count, GFP_KERNEL); + if (!skb) { + BT_ERR("cannot allocate memory for HCILL packet"); + err = -ENOMEM; + goto out; + } + + skb_put_data(skb, buf, count); + + err = ai2_send_frame(ai2dev, skb); + if (err) + goto out; + + return count; +out: + return err; +} + +static const struct gnss_operations gnss_ai2_ops = { + .open = gnss_ai2_open, + .close = gnss_ai2_close, + .write_raw = gnss_ai2_write_raw, +}; + +static void process_ai2_packet(struct ai2_device *ai2dev, + u8 cmd, u8 *data, u16 len) +{ + if (cmd != AI2_REPORT_NMEA) + return; + + if (len <= NMEA_HEADER_LEN) + return; + + len -= NMEA_HEADER_LEN; + data += NMEA_HEADER_LEN; + + gnss_insert_raw(ai2dev->gdev, data, len); +} + +/* do some sanity checks and split frame into packets */ +static void process_ai2_frame(struct ai2_device *ai2dev) +{ + u16 sum; + int i; + u8 *head; + u8 *data; + + sum = 0; + data = ai2dev->recv_skb->data; + for (i = 0; i < ai2dev->recv_skb->len - 2; i++) + sum += data[i]; + + print_hex_dump_bytes("ai2 frame: ", DUMP_PREFIX_OFFSET, data, ai2dev->recv_skb->len); + + if (get_unaligned_le16(data + i) != sum) { + dev_dbg(ai2dev->dev, + "checksum error in reception, dropping frame\n"); + return; + } + + /* reached if byte 1 in the command packet is set to 1 */ + if (data[1] == AI2_ACK) + return; + + head = skb_pull(ai2dev->recv_skb, 2); /* drop frame start marker */ + while (head && (ai2dev->recv_skb->len >= 3)) { + u8 cmd; + u16 pktlen; + + cmd = head[0]; + pktlen = get_unaligned_le16(head + 1); + data = skb_pull(ai2dev->recv_skb, 3); + if (!data) + break; + + if (pktlen > ai2dev->recv_skb->len) + break; + + head = skb_pull(ai2dev->recv_skb, pktlen); + + process_ai2_packet(ai2dev, cmd, data, pktlen); + } +} + +static void process_ai2_data(struct ai2_device *ai2dev, + u8 *data, int len) +{ + int i; + + for (i = 0; i < len; i++) { + if (!ai2dev->recv_skb) { + ai2dev->recv_esc = false; + if (data[i] != AI2_ESCAPE) { + dev_dbg(ai2dev->dev, "dropping data, trying to resync\n"); + continue; + } + ai2dev->recv_skb = alloc_skb(MAX_AI2_FRAME_SIZE, GFP_KERNEL); + if (!ai2dev->recv_skb) + return; + + dev_dbg(ai2dev->dev, "starting packet\n"); + + /* this initial AI2_ESCAPE is part of checksum computation */ + skb_put_u8(ai2dev->recv_skb, data[i]); + continue; + } + if (ai2dev->recv_skb->len == 1) { + if (data[i] == AI2_END_MARKER) { + dev_dbg(ai2dev->dev, "unexpected end of frame received\n"); + kfree_skb(ai2dev->recv_skb); + ai2dev->recv_skb = NULL; + continue; + } + skb_put_u8(ai2dev->recv_skb, data[i]); + } else { + /* drop one of two AI2_ESCAPE */ + if ((!ai2dev->recv_esc) && + (data[i] == AI2_ESCAPE)) { + ai2dev->recv_esc = true; + continue; + } + + if (ai2dev->recv_esc && + (data[i] == AI2_END_MARKER)) { + process_ai2_frame(ai2dev); + kfree_skb(ai2dev->recv_skb); + ai2dev->recv_skb = NULL; + continue; + } + + ai2dev->recv_esc = false; + skb_put_u8(ai2dev->recv_skb, data[i]); + } + } +} + +static void gnss_recv_frame(struct device *dev, struct sk_buff *skb) +{ + struct ai2_device *ai2dev = dev_get_drvdata(dev); + struct gps_event_hdr *gnss_hdr; + u8 *data; + + if (!ai2dev->gdev) { + kfree_skb(skb); + return; + } + + gnss_hdr = (struct gps_event_hdr *)skb->data; + + data = skb_pull(skb, sizeof(*gnss_hdr)); + /* + * REVISIT: maybe do something with the completed + * event + */ + if (gnss_hdr->opcode == GPS_CH9_OP_READ) { + mutex_lock(&ai2dev->gdev_mutex); + if (ai2dev->gdev_open) { + if (ai2raw) + gnss_insert_raw(ai2dev->gdev, data, skb->len); + else + process_ai2_data(ai2dev, data, skb->len); + } else { + dev_dbg(ai2dev->dev, + "receiving data while chip should be off\n"); + } + mutex_unlock(&ai2dev->gdev_mutex); + } + kfree_skb(skb); +} + +static int gnss_ai2_probe(struct platform_device *pdev) +{ + struct gnss_device *gdev; + struct ai2_device *ai2dev; + int ret; + + ai2dev = devm_kzalloc(&pdev->dev, sizeof(*ai2dev), GFP_KERNEL); + if (!ai2dev) + return -ENOMEM; + + ai2dev->dev = &pdev->dev; + gdev = gnss_allocate_device(&pdev->dev); + if (!gdev) + return -ENOMEM; + + gdev->ops = &gnss_ai2_ops; + gdev->type = ai2raw ? GNSS_TYPE_AI2 : GNSS_TYPE_NMEA; + gnss_set_drvdata(gdev, ai2dev); + platform_set_drvdata(pdev, ai2dev); + st_set_gnss_recv_func(pdev->dev.parent, gnss_recv_frame); + mutex_init(&ai2dev->gdev_mutex); + + ret = gnss_register_device(gdev); + if (ret) + goto err; + + ai2dev->gdev = gdev; + return 0; + +err: + st_set_gnss_recv_func(pdev->dev.parent, NULL); + + if (ai2dev->recv_skb) + kfree_skb(ai2dev->recv_skb); + + gnss_put_device(gdev); + return ret; +} + +static void gnss_ai2_remove(struct platform_device *pdev) +{ + struct ai2_device *ai2dev = platform_get_drvdata(pdev); + + st_set_gnss_recv_func(pdev->dev.parent, NULL); + gnss_deregister_device(ai2dev->gdev); + gnss_put_device(ai2dev->gdev); + if (ai2dev->recv_skb) + kfree_skb(ai2dev->recv_skb); +} + +static const struct platform_device_id gnss_ai2_id[] = { + { + .name = "ti-ai2-gnss" + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(platform, gnss_ai2_id); + +static struct platform_driver gnss_ai2_driver = { + .driver = { + .name = "gnss-ai2", + }, + .probe = gnss_ai2_probe, + .remove_new = gnss_ai2_remove, + .id_table = gnss_ai2_id, +}; +module_platform_driver(gnss_ai2_driver); + +module_param(ai2raw, bool, 0600); +MODULE_DESCRIPTION("AI2 GNSS driver"); +MODULE_LICENSE("GPL"); From patchwork Mon Jun 3 14:44:00 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andreas Kemnade X-Patchwork-Id: 801747 Received: from mail2.andi.de1.cc (vmd64148.contaboserver.net [161.97.139.27]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5E488132110; Mon, 3 Jun 2024 14:44:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=161.97.139.27 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1717425876; cv=none; b=t0tX9/E330pgkdzJT8hwh+adTmZLuO8KWqln4+iM3LOQOARyFlgxbLWXpPdV7vSUcaQ1Vm+BrnBsY0Pzx1lPSRiWhsMmRfaZ7cUbs+hoBZ4k9+Kmg9osJEUCDt5KubV9jtV6wB6DnPhjF4YZI9UP3VbcRw9jzFsMAp+QysgCAWs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1717425876; c=relaxed/simple; bh=nMNg8q3S5J9D9ytoDoOy6OuUruVXINyBaiIQVaqduVA=; h=From:To:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=eqGQxs0k7riMSQ6qAdoHWdUwnGlRxaWBm8POyTXUEMj4l7JOJRFkP7i54iiPNSRCX1NSjM1UnnLenpiGJDLpmycLU8548R9cLYGDN9x7xM179lkAtljLxjzLEqFkLjKd/PYQQoqJryC3LS+90ausW21tYp8X1HjMTyjVLvKSfYQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=kemnade.info; spf=pass smtp.mailfrom=kemnade.info; dkim=pass (2048-bit key) header.d=kemnade.info header.i=@kemnade.info header.b=45L9c8K5; arc=none smtp.client-ip=161.97.139.27 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=kemnade.info Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=kemnade.info Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kemnade.info header.i=@kemnade.info header.b="45L9c8K5" Received: from mail.andi.de1.cc ([2a02:c205:3004:2154::1]) by mail2.andi.de1.cc with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1sE8vB-008hAw-2v; Mon, 03 Jun 2024 16:44:27 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=kemnade.info; s=20220719; h=Content-Transfer-Encoding:MIME-Version: References:In-Reply-To:Message-Id:Date:Subject:To:From:Sender:Reply-To:Cc: Content-Type:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help: List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive; bh=Fk7G13HeRimY6DRpUituC/NmHzEEmRRr1a/ukg9SBGM=; b=45L9c8K5xSufwauaZZEQ5Ezfpp HQGoHopTEzBWCystnXWBZ9n70yVN3n+eJTT0GIyadQWPti4VspeQ733X6KD06RMHZA5Whwvcs5C8g QP+n+LHoUD9icF69QeK79FAQpd/SSLocdOBs+BTImX01eFzsq06AfmKu0QHkApxb15z1nRIoqnbGL T22328hYwn30Eb2E9z/Hx13ZYy9JhKcsCuQUMFW/1bpeojfcgUEE9AWB+XtAWARJ6CsTdegYsK80W hwQYx0QYYerG0XxALNH8xHvNl/JpHqcqNG+TvSSPoTGmEVh6aDQ530YlXeu75vpbNciq1IDkLu1Ka wkGKsJsg==; Received: from p200300c20737c2001a3da2fffebfd33a.dip0.t-ipconnect.de ([2003:c2:737:c200:1a3d:a2ff:febf:d33a] helo=aktux) by mail.andi.de1.cc with esmtpsa (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1sE8vA-002UfX-1K; Mon, 03 Jun 2024 16:44:25 +0200 Received: from andi by aktux with local (Exim 4.96) (envelope-from ) id 1sE8vB-009DB2-1P; Mon, 03 Jun 2024 16:44:25 +0200 From: Andreas Kemnade To: marcel@holtmann.org, luiz.dentz@gmail.com, johan@kernel.org, gregkh@linuxfoundation.org, jirislaby@kernel.org, andreas@kemnade.info, pmenzel@molgen.mpg.de, linux-kernel@vger.kernel.org, linux-bluetooth@vger.kernel.org, Adam Ford , Tony Lindgren , tomi.valkeinen@ideasonboard.com, =?utf-8?q?P=C3=A9ter_Ujfalusi?= , robh@kernel.org, hns@goldelico.com Subject: [PATCH RFC v3 4/4] gnss: ai2: replace long sleeps by wait for acks Date: Mon, 3 Jun 2024 16:44:00 +0200 Message-Id: <20240603144400.2195564-5-andreas@kemnade.info> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240603144400.2195564-1-andreas@kemnade.info> References: <20240603144400.2195564-1-andreas@kemnade.info> Precedence: bulk X-Mailing-List: linux-bluetooth@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Previously there were long sleeps for everything sent out. Replace the sleeps by some wait for completion. Wait times like 60ms are seen. There are ack packets sent out if requested. Unfortunately just waiting for them seems not stable, some open()/close() loop stress-testing brings the communication into complete disorder. Unfortunately these ack packets arrive before a complete answer of the command has been received but apparently after some processing has been done. Properly declaring expected answers might help but adding that can only be justified after some wider testing. So leaving this part of the series as a RFC and base for future optimzations. Signed-off-by: Andreas Kemnade --- drivers/gnss/ai2.c | 111 +++++++++++++++++++++++++++++---------------- 1 file changed, 72 insertions(+), 39 deletions(-) diff --git a/drivers/gnss/ai2.c b/drivers/gnss/ai2.c index 0cc21b64b7c3f..19597ee891881 100644 --- a/drivers/gnss/ai2.c +++ b/drivers/gnss/ai2.c @@ -5,6 +5,8 @@ * * Copyright (C) 2024 Andreas Kemnade */ +#define DEBUG +#include #include #include #include @@ -67,6 +69,16 @@ struct ai2_device { struct device *dev; struct sk_buff *recv_skb; bool recv_esc; + /* + * completion for the lower level around + * GPS_CH9_OP_COMPLETED_EVT + * probably more important if we send large + * fragmented packets + */ + struct completion ch9_complete; + + /* completion for AI2 ack packets */ + struct completion ai2_ack_complete; }; static struct sk_buff *ai2_skb_alloc(unsigned int len, gfp_t how) @@ -86,6 +98,7 @@ static int ai2_send_frame(struct ai2_device *ai2dev, int len; struct gps_event_hdr *gnssdrv_hdr; struct hci_dev *hdev; + int ret; if (skb->len >= U16_MAX) return -EINVAL; @@ -95,13 +108,25 @@ static int ai2_send_frame(struct ai2_device *ai2dev, * not needed for simple config commands */ len = skb->len; + print_hex_dump_bytes("ai2 send frame: ", DUMP_PREFIX_OFFSET, skb->data, skb->len); + gnssdrv_hdr = skb_push(skb, sizeof(struct gps_event_hdr)); gnssdrv_hdr->opcode = GPS_CH9_OP_WRITE; gnssdrv_hdr->plen = __cpu_to_le16(len); - hci_skb_pkt_type(skb) = GPS_CH9_PKT_NUMBER; hdev = st_get_hci(ai2dev->dev->parent); - return hdev->send(hdev, skb); + reinit_completion(&ai2dev->ch9_complete); + + ret = hdev->send(hdev, skb); + if (ret) + return ret; + + if (!wait_for_completion_timeout(&ai2dev->ch9_complete, + msecs_to_jiffies(2000))) + return -ETIMEDOUT; + dev_dbg(ai2dev->dev, "send finished\n"); + + return 0; } static void ai2_put_escaped(struct sk_buff *skb, u8 d) @@ -150,30 +175,50 @@ static struct sk_buff *ai2_compose_frame(bool request_ack, return skb; } -static int ai2_set_receiver_state(struct ai2_device *ai2dev, - uint8_t state) +static int ai2_compose_send_frame(struct ai2_device *ai2dev, + bool request_ack, + u8 cmd, + const u8 *data, + int len) { - struct sk_buff *skb = ai2_compose_frame(true, AI2_CMD_RECEIVER_STATE, - &state, 1); + struct sk_buff *skb = ai2_compose_frame(request_ack, cmd, data, len); if (!skb) return -ENOMEM; + if (request_ack) { + int ret; + + reinit_completion(&ai2dev->ai2_ack_complete); + + ret = ai2_send_frame(ai2dev, skb); + if (ret) + return ret; + + if (!wait_for_completion_timeout(&ai2dev->ai2_ack_complete, + msecs_to_jiffies(2000))) + return -ETIMEDOUT; + + return 0; + } + return ai2_send_frame(ai2dev, skb); } +static int ai2_set_receiver_state(struct ai2_device *ai2dev, + uint8_t state) +{ + return ai2_compose_send_frame(ai2dev, true, AI2_CMD_RECEIVER_STATE, + &state, 1); +} + static int ai2_config_nmea_reports(struct ai2_device *ai2dev, uint8_t mask) { u8 buf[4] = {0}; - struct sk_buff *skb; buf[0] = mask; - skb = ai2_compose_frame(true, AI2_CMD_CONFIG_NMEA, - buf, sizeof(buf)); - if (!skb) - return -ENOMEM; - - return ai2_send_frame(ai2dev, skb); + return ai2_compose_send_frame(ai2dev, true, AI2_CMD_CONFIG_NMEA, + buf, sizeof(buf)); } /* @@ -186,22 +231,12 @@ static int gnss_ai2_init(struct ai2_device *ai2dev) { int ret; u8 d = 0x01; - struct sk_buff *skb = ai2_compose_frame(true, 0xf5, &d, 1); - - if (!skb) - return -ENOMEM; - - ret = ai2_send_frame(ai2dev, skb); + ret = ai2_compose_send_frame(ai2dev, true, 0xf5, &d, 1); if (ret) return ret; - msleep(200); /* seen some 60ms response time here, so wait a bit */ d = 5; - skb = ai2_compose_frame(true, 0xf1, &d, 1); - if (!skb) - return -ENOMEM; - - return ai2_send_frame(ai2dev, skb); + return ai2_compose_send_frame(ai2dev, true, 0xf1, &d, 1); } static int gnss_ai2_open(struct gnss_device *gdev) @@ -219,18 +254,14 @@ static int gnss_ai2_open(struct gnss_device *gdev) if (ret) goto err; - /* TODO: find out on what kind of ack we should wait */ - msleep(50); ret = ai2_set_receiver_state(ai2dev, RECEIVER_STATE_IDLE); if (ret) goto err; - msleep(100); ret = ai2_config_nmea_reports(ai2dev, NMEA_MASK_ALL); if (ret) goto err; - msleep(50); ret = ai2_set_receiver_state(ai2dev, RECEIVER_STATE_ON); if (ret) goto err; @@ -253,13 +284,9 @@ static void gnss_ai2_close(struct gnss_device *gdev) { struct ai2_device *ai2dev = gnss_get_drvdata(gdev); - /* TODO: find out on what kind of ack we should wait */ if (!ai2raw) { - msleep(50); ai2_set_receiver_state(ai2dev, RECEIVER_STATE_IDLE); - msleep(50); ai2_set_receiver_state(ai2dev, RECEIVER_STATE_OFF); - msleep(200); /* seen some longer response time here, so wait */ } mutex_lock(&ai2dev->gdev_mutex); @@ -344,8 +371,10 @@ static void process_ai2_frame(struct ai2_device *ai2dev) } /* reached if byte 1 in the command packet is set to 1 */ - if (data[1] == AI2_ACK) + if (data[1] == AI2_ACK) { + complete(&ai2dev->ai2_ack_complete); return; + } head = skb_pull(ai2dev->recv_skb, 2); /* drop frame start marker */ while (head && (ai2dev->recv_skb->len >= 3)) { @@ -433,11 +462,9 @@ static void gnss_recv_frame(struct device *dev, struct sk_buff *skb) gnss_hdr = (struct gps_event_hdr *)skb->data; data = skb_pull(skb, sizeof(*gnss_hdr)); - /* - * REVISIT: maybe do something with the completed - * event - */ - if (gnss_hdr->opcode == GPS_CH9_OP_READ) { + + switch (gnss_hdr->opcode) { + case GPS_CH9_OP_READ: mutex_lock(&ai2dev->gdev_mutex); if (ai2dev->gdev_open) { if (ai2raw) @@ -449,6 +476,10 @@ static void gnss_recv_frame(struct device *dev, struct sk_buff *skb) "receiving data while chip should be off\n"); } mutex_unlock(&ai2dev->gdev_mutex); + break; + case GPS_CH9_OP_COMPLETED_EVT: + complete(&ai2dev->ch9_complete); + break; } kfree_skb(skb); } @@ -474,6 +505,8 @@ static int gnss_ai2_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ai2dev); st_set_gnss_recv_func(pdev->dev.parent, gnss_recv_frame); mutex_init(&ai2dev->gdev_mutex); + init_completion(&ai2dev->ch9_complete); + init_completion(&ai2dev->ai2_ack_complete); ret = gnss_register_device(gdev); if (ret)