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