[RFC net-next 15/15] net: cpc: add Bluetooth HCI driver

Damien Riégel posted 15 patches 7 months, 1 week ago
[RFC net-next 15/15] net: cpc: add Bluetooth HCI driver
Posted by Damien Riégel 7 months, 1 week ago
Add support for Bluetooth HCI driver. As most of the protocol is already
handled by the remote endpoint, this driver is just doing some glue to
plug into CPC.

Signed-off-by: Damien Riégel <damien.riegel@silabs.com>
---
 drivers/net/cpc/Kconfig  |   2 +-
 drivers/net/cpc/Makefile |   2 +-
 drivers/net/cpc/ble.c    | 147 +++++++++++++++++++++++++++++++++++++++
 drivers/net/cpc/ble.h    |  14 ++++
 drivers/net/cpc/main.c   |  23 ++++--
 5 files changed, 181 insertions(+), 7 deletions(-)
 create mode 100644 drivers/net/cpc/ble.c
 create mode 100644 drivers/net/cpc/ble.h

diff --git a/drivers/net/cpc/Kconfig b/drivers/net/cpc/Kconfig
index f5159390a82..e8faa351bf7 100644
--- a/drivers/net/cpc/Kconfig
+++ b/drivers/net/cpc/Kconfig
@@ -2,7 +2,7 @@
 
 menuconfig CPC
 	tristate "Silicon Labs Co-Processor Communication (CPC) Protocol"
-	depends on NET && SPI
+	depends on NET && SPI && BT
 	select CRC_ITU_T
 	help
 	  Provide support for the CPC protocol to Silicon Labs EFR32 devices.
diff --git a/drivers/net/cpc/Makefile b/drivers/net/cpc/Makefile
index 195cdf4ad62..cee40aec412 100644
--- a/drivers/net/cpc/Makefile
+++ b/drivers/net/cpc/Makefile
@@ -1,5 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0
 
-cpc-y := endpoint.o header.o interface.o main.o protocol.o spi.o system.o
+cpc-y := ble.o endpoint.o header.o interface.o main.o protocol.o spi.o system.o
 
 obj-$(CONFIG_CPC)	+= cpc.o
diff --git a/drivers/net/cpc/ble.c b/drivers/net/cpc/ble.c
new file mode 100644
index 00000000000..2b7aec4dbdf
--- /dev/null
+++ b/drivers/net/cpc/ble.c
@@ -0,0 +1,147 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for Bluetooth HCI over CPC.
+ *
+ * Copyright (c) 2025, Silicon Laboratories, Inc.
+ */
+
+#include <linux/skbuff.h>
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "ble.h"
+#include "cpc.h"
+
+struct cpc_ble {
+	struct cpc_endpoint *ep;
+	struct hci_dev *hdev;
+	struct sk_buff_head txq;
+};
+
+static int cpc_ble_open(struct hci_dev *hdev)
+{
+	struct cpc_ble *ble = hci_get_drvdata(hdev);
+
+	skb_queue_head_init(&ble->txq);
+
+	return cpc_endpoint_connect(ble->ep);
+}
+
+static int cpc_ble_close(struct hci_dev *hdev)
+{
+	struct cpc_ble *ble = hci_get_drvdata(hdev);
+
+	cpc_endpoint_disconnect(ble->ep);
+
+	skb_queue_purge(&ble->txq);
+
+	return 0;
+}
+
+static int cpc_ble_flush(struct hci_dev *hdev)
+{
+	struct cpc_ble *ble = hci_get_drvdata(hdev);
+
+	skb_queue_purge(&ble->txq);
+
+	return 0;
+}
+
+static int cpc_ble_send(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	struct cpc_ble *ble = hci_get_drvdata(hdev);
+
+	memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
+
+	return cpc_endpoint_write(ble->ep, skb);
+}
+
+static void cpc_ble_rx_frame(struct cpc_endpoint *ep, struct sk_buff *skb)
+{
+	struct cpc_ble *ble = cpc_endpoint_get_drvdata(ep);
+
+	hci_skb_pkt_type(skb) = *((u8 *)skb_pull_data(skb, 1));
+	hci_skb_expect(skb) = skb->len;
+
+	hci_recv_frame(ble->hdev, skb);
+}
+
+static struct cpc_endpoint_ops cpc_ble_ops = {
+	.rx = cpc_ble_rx_frame,
+};
+
+static int cpc_ble_probe(struct cpc_endpoint *ep)
+{
+	struct cpc_ble *ble;
+	int err;
+
+	ble = kzalloc(sizeof(*ble), GFP_KERNEL);
+	if (!ble) {
+		err = -ENOMEM;
+		goto alloc_ble_fail;
+	}
+
+	cpc_endpoint_set_ops(ep, &cpc_ble_ops);
+	cpc_endpoint_set_drvdata(ep, ble);
+
+	ble->ep = ep;
+	ble->hdev = hci_alloc_dev();
+	if (!ble->hdev) {
+		err = -ENOMEM;
+		goto alloc_hdev_fail;
+	}
+
+	hci_set_drvdata(ble->hdev, ble);
+	ble->hdev->open = cpc_ble_open;
+	ble->hdev->close = cpc_ble_close;
+	ble->hdev->flush = cpc_ble_flush;
+	ble->hdev->send = cpc_ble_send;
+
+	err = hci_register_dev(ble->hdev);
+	if (err)
+		goto register_hdev_fail;
+
+	return 0;
+
+register_hdev_fail:
+	hci_free_dev(ble->hdev);
+alloc_hdev_fail:
+	kfree(ble);
+alloc_ble_fail:
+	return err;
+}
+
+static void cpc_ble_remove(struct cpc_endpoint *ep)
+{
+	struct cpc_ble *ble = cpc_endpoint_get_drvdata(ep);
+
+	hci_unregister_dev(ble->hdev);
+	hci_free_dev(ble->hdev);
+	kfree(ble);
+}
+
+static struct cpc_driver ble_driver = {
+	.driver = {
+		.name = CPC_BLUETOOTH_ENDPOINT_NAME,
+	},
+	.probe = cpc_ble_probe,
+	.remove = cpc_ble_remove,
+};
+
+/**
+ * cpc_ble_drv_register - Register the ble endpoint driver.
+ *
+ * @return: 0 on success, otherwise a negative error code.
+ */
+int cpc_ble_drv_register(void)
+{
+	return cpc_driver_register(&ble_driver);
+}
+
+/**
+ * cpc_ble_drv_unregister - Unregister the ble endpoint driver.
+ */
+void cpc_ble_drv_unregister(void)
+{
+	cpc_driver_unregister(&ble_driver);
+}
diff --git a/drivers/net/cpc/ble.h b/drivers/net/cpc/ble.h
new file mode 100644
index 00000000000..ae1cac4e7e8
--- /dev/null
+++ b/drivers/net/cpc/ble.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2025, Silicon Laboratories, Inc.
+ */
+
+#ifndef __CPC_BLE_H
+#define __CPC_BLE_H
+
+#define CPC_BLUETOOTH_ENDPOINT_NAME "silabs,cpc-ble"
+
+int cpc_ble_drv_register(void);
+void cpc_ble_drv_unregister(void);
+
+#endif
diff --git a/drivers/net/cpc/main.c b/drivers/net/cpc/main.c
index b4e73145ac2..e5636207d5d 100644
--- a/drivers/net/cpc/main.c
+++ b/drivers/net/cpc/main.c
@@ -7,6 +7,7 @@
 #include <linux/module.h>
 
 #include "cpc.h"
+#include "ble.h"
 #include "header.h"
 #include "spi.h"
 #include "system.h"
@@ -125,13 +126,24 @@ static int __init cpc_init(void)
 
 	err = cpc_system_drv_register();
 	if (err)
-		bus_unregister(&cpc_bus);
+		goto unregister_bus;
+
+	err = cpc_ble_drv_register();
+	if (err)
+		goto unregister_system_driver;
 
 	err = cpc_spi_register_driver();
-	if (err) {
-		cpc_system_drv_unregister();
-		bus_unregister(&cpc_bus);
-	}
+	if (err)
+		goto unregister_ble_driver;
+
+	return 0;
+
+unregister_ble_driver:
+	cpc_ble_drv_unregister();
+unregister_system_driver:
+	cpc_system_drv_unregister();
+unregister_bus:
+	bus_unregister(&cpc_bus);
 
 	return err;
 }
@@ -140,6 +152,7 @@ module_init(cpc_init);
 static void __exit cpc_exit(void)
 {
 	cpc_spi_unregister_driver();
+	cpc_ble_drv_unregister();
 	cpc_system_drv_unregister();
 	bus_unregister(&cpc_bus);
 }
-- 
2.49.0