Skip to contentSkip to conversation

Realtek Bluetooth serdev support (H5 protocol)

RFC New Martin Blumenstingl 6 participants 41 messages Jan 1, 2018 – Jan 9, 2018

Subsystems (6)
lore
Share & tools
lore

Martin Blumenstingl

author reviewer
#0 lore raw

v2 cover Realtek Bluetooth serdev support (H5 protocol)

Hello,

I am sending this series because I want to get feedback. please don't expect it to be perfect (yet). especially since it's touching parts of the kernel I've not worked with before.

my goal was NOT to use the user-space initialization tool for the Bluetooth part of one of my devices (which uses a Realtek RTL8723BS). my hope was that I only had to hook up serdev support to hci_h5 and re-use the setup function provided by btrtl. unfortunately it wasn't that easy.

there are no datasheets for the RTL8723BS or RTL8723DS out there. however, there are some userspace tools which go through the whole initialization process - this is what I used as reference:
- RTL8723BS Bluetooth sources from [0]
- RTL8723DS Bluetooth sources from [1]

These modules require a firmware and a config file (both don't seem to be compatible with what we have in linux-firmware at the moment).

since it's an RFC I also have some questions:
- who can help testing that I didn't break any USB based Realtek Bluetooth chips? I only have UART (embedded into the SDIO RTL8723BS and RTL8723DS) ones
- what about the Bluetooth firmware and config blobs? how to get them into the linux-firmware tree? maybe Larry Finger can help here :)
- what are <your name here> comments about this series?

Changes since RFC v1 at [2]:
- fixed compile error in PATCH #1 if CONFIG_SERIAL_DEV_BUS is disabled (spotted by Intel's kbuild test robot - many thanks!)
- renamed "disable-gpios" to "enable-gpios" in the dt-bindings and hci_h5 patches
- added a "realtek,config-data" property to devicetree which can be used to provide the "config"-blob. I did not find a way to auto-generate that blob. a similar property is described by "marvell-bt-8xxx.txt". This affects the dt-bindings (Documentation) patch and introduces a new patch for btrtl.c.
- guard against the vendor_setup callback being NULL (thanks Marcel Holtmann for suggesting this in the review of RFC 1) in the hci_h5 patch
- added comments where the msleep() times are coming from in the hci_h5 patch
- dropped an unnecessary msleep() after sending the "change baudrate" command (thanks Marcel Holtmann for spotting this)
- entirely drop HCI_UART_INIT_PENDING instead of just ignoring it in hci_serdev

[0] https://github.com/lwfinger/rtl8723bs_bt
[1] https://github.com/NextThingCo/rtl8723ds_bt
[2] https://www.spinics.net/lists/linux-bluetooth/msg72862.html

Martin Blumenstingl (9):
serdev: implement parity configuration
dt-bindings: net: bluetooth: add support for Realtek Bluetooth chips
Bluetooth: btrtl: add MODULE_FIRMWARE declarations
Bluetooth: btrtl: split the device initialization into smaller parts
Bluetooth: btrtl: add support for retrieving the UART settings
Bluetooth: btrtl: add support for the RTL8723BS and RTL8723DS chips
bluetooth: btrtl: load the config blob from devicetree when available
Bluetooth: drop HCI_UART_INIT_PENDING support
Bluetooth: hci_h5: add support for Realtek UART Bluetooth modules

 .../devicetree/bindings/net/realtek-bluetooth.txt  |  41 ++
 drivers/bluetooth/Kconfig                          |   1 +
 drivers/bluetooth/btrtl.c                          | 461 ++++++++++++++++-----
 drivers/bluetooth/btrtl.h                          |  46 ++
 drivers/bluetooth/hci_h5.c                         | 206 ++++++++-
 drivers/bluetooth/hci_ldisc.c                      |  38 --
 drivers/bluetooth/hci_serdev.c                     |   3 -
 drivers/bluetooth/hci_uart.h                       |   4 +-
 drivers/tty/serdev/core.c                          |  12 +
 drivers/tty/serdev/serdev-ttyport.c                |  21 +
 include/linux/serdev.h                             |   5 +
 11 files changed, 678 insertions(+), 160 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/net/realtek-bluetooth.txt
Signature · 1 line
2.15.1
author reviewer
#1 lore raw

RFC v2 1/9 serdev: implement parity configuration

Some Bluetooth modules (for example the ones found in Realtek RTL8723BS and RTL8723DS) want to communicate with the host with even parity enabled.
Add a new function and the corresponding internal callbacks so parity can be configured. This supports enabling and disabling parity as well as setting the type to odd or even.

---
 drivers/tty/serdev/core.c           | 12 ++++++++++++
 drivers/tty/serdev/serdev-ttyport.c | 21 +++++++++++++++++++++
 include/linux/serdev.h              |  5 +++++
 3 files changed, 38 insertions(+)
drivers/tty/serdev/core.c +12 -0
index 1bef39828ca7..d327b02980f5 100644
@@ -225,6 +225,18 @@ void serdev_device_set_flow_control(struct serdev_device *serdev, bool enable)
 }
 EXPORT_SYMBOL_GPL(serdev_device_set_flow_control);
 
+void serdev_device_set_parity(struct serdev_device *serdev, bool enable,
+			      bool odd)
+{
+	struct serdev_controller *ctrl = serdev->ctrl;
+
+	if (!ctrl || !ctrl->ops->set_parity)
+		return;
+
+	ctrl->ops->set_parity(ctrl, enable, odd);
+}
+EXPORT_SYMBOL_GPL(serdev_device_set_parity);
+
 void serdev_device_wait_until_sent(struct serdev_device *serdev, long timeout)
 {
 	struct serdev_controller *ctrl = serdev->ctrl;
drivers/tty/serdev/serdev-ttyport.c +21 -0
index 247788a16f0b..36bed86e5c96 100644
@@ -190,6 +190,26 @@ static void ttyport_set_flow_control(struct serdev_controller *ctrl, bool enable
 	tty_set_termios(tty, &ktermios);
 }
 
+static void ttyport_set_parity(struct serdev_controller *ctrl, bool enable,
+			       bool odd)
+{
+	struct serport *serport = serdev_controller_get_drvdata(ctrl);
+	struct tty_struct *tty = serport->tty;
+	struct ktermios ktermios = tty->termios;
+
+	if (enable)
+		ktermios.c_cflag |= PARENB;
+	else
+		ktermios.c_cflag &= ~PARENB;
+
+	if (odd)
+		ktermios.c_cflag |= PARODD;
+	else
+		ktermios.c_cflag &= ~PARODD;
+
+	tty_set_termios(tty, &ktermios);
+}
+
 static void ttyport_wait_until_sent(struct serdev_controller *ctrl, long timeout)
 {
 	struct serport *serport = serdev_controller_get_drvdata(ctrl);
@@ -227,6 +247,7 @@ static const struct serdev_controller_ops ctrl_ops = {
 	.open = ttyport_open,
 	.close = ttyport_close,
 	.set_flow_control = ttyport_set_flow_control,
+	.set_parity = ttyport_set_parity,
 	.set_baudrate = ttyport_set_baudrate,
 	.wait_until_sent = ttyport_wait_until_sent,
 	.get_tiocm = ttyport_get_tiocm,
include/linux/serdev.h +5 -0
index d609e6dc5bad..07f2d4d4e88c 100644
@@ -86,6 +86,7 @@ struct serdev_controller_ops {
 	int (*open)(struct serdev_controller *);
 	void (*close)(struct serdev_controller *);
 	void (*set_flow_control)(struct serdev_controller *, bool);
+	void (*set_parity)(struct serdev_controller *, bool, bool);
 	unsigned int (*set_baudrate)(struct serdev_controller *, unsigned int);
 	void (*wait_until_sent)(struct serdev_controller *, long);
 	int (*get_tiocm)(struct serdev_controller *);
@@ -195,6 +196,7 @@ int serdev_device_open(struct serdev_device *);
 void serdev_device_close(struct serdev_device *);
 unsigned int serdev_device_set_baudrate(struct serdev_device *, unsigned int);
 void serdev_device_set_flow_control(struct serdev_device *, bool);
+void serdev_device_set_parity(struct serdev_device *, bool, bool);
 int serdev_device_write_buf(struct serdev_device *, const unsigned char *, size_t);
 void serdev_device_wait_until_sent(struct serdev_device *, long);
 int serdev_device_get_tiocm(struct serdev_device *);
@@ -237,6 +239,9 @@ static inline unsigned int serdev_device_set_baudrate(struct serdev_device *sdev
 	return 0;
 }
 static inline void serdev_device_set_flow_control(struct serdev_device *sdev, bool enable) {}
+static inline void serdev_device_set_parity(struct serdev_device *sdev,
+					    bool enable,
+					    bool odd) {}
 static inline int serdev_device_write_buf(struct serdev_device *serdev,
 					  const unsigned char *buf,
 					  size_t count)
Signature · 1 line
2.15.1
maintainer
#2 lore raw

RFC v2 1/9 serdev: implement parity configuration

Hi Martin,

Quoted 24 lines

Some Bluetooth modules (for example the ones found in Realtek RTL8723BS and RTL8723DS) want to communicate with the host with even parity enabled.
Add a new function and the corresponding internal callbacks so parity can be configured. This supports enabling and disabling parity as well as setting the type to odd or even.

---

drivers/tty/serdev/core.c           | 12 ++++++++++++
drivers/tty/serdev/serdev-ttyport.c | 21 +++++++++++++++++++++
include/linux/serdev.h              |  5 +++++
3 files changed, 38 insertions(+)
drivers/tty/serdev/core.c +2 -0
index 1bef39828ca7..d327b02980f5 100644
@@ -225,6 +225,18 @@ void serdev_device_set_flow_control(struct serdev_device *serdev, bool enable)
 }
 EXPORT_SYMBOL_GPL(serdev_device_set_flow_control);
 
+void serdev_device_set_parity(struct serdev_device *serdev, bool enable,
+			      bool odd)

drivers/tty/serdev/core.c void serdev_device_set_flow_control(struct serdev_device *serdev, bool enable) · line 239

Quoted 10 lines
drivers/tty/serdev/core.c +10 -0
+{
+	struct serdev_controller *ctrl = serdev->ctrl;
+
+	if (!ctrl || !ctrl->ops->set_parity)
+		return;
+
+	ctrl->ops->set_parity(ctrl, enable, odd);
+}
+EXPORT_SYMBOL_GPL(serdev_device_set_parity);
+

this really needs Rob?s ACK before I take the patch.

Regards

Marcel

author reviewer
#3 lore raw

RFC v2 1/9 serdev: implement parity configuration

Hi Marcel, Hi Rob,

Quoted 24 lines · Marcel Holtmann

Hi Martin,

Quoted 22 lines

Some Bluetooth modules (for example the ones found in Realtek RTL8723BS and RTL8723DS) want to communicate with the host with even parity enabled.
Add a new function and the corresponding internal callbacks so parity can be configured. This supports enabling and disabling parity as well as setting the type to odd or even.

---

drivers/tty/serdev/core.c           | 12 ++++++++++++
drivers/tty/serdev/serdev-ttyport.c | 21 +++++++++++++++++++++
include/linux/serdev.h              |  5 +++++
3 files changed, 38 insertions(+)
drivers/tty/serdev/core.c +0 -0
index 1bef39828ca7..d327b02980f5 100644
@@ -225,6 +225,18 @@ void serdev_device_set_flow_control(struct serdev_device *serdev, bool enable)
 }
 EXPORT_SYMBOL_GPL(serdev_device_set_flow_control);
 
Quoted 14 lines

drivers/tty/serdev/core.c void serdev_device_set_flow_control(struct serdev_device *serdev, bool enable) · line 239

Quoted 12 lines
drivers/tty/serdev/core.c +12 -0
+void serdev_device_set_parity(struct serdev_device *serdev, bool enable,
+                           bool odd)
+{
+     struct serdev_controller *ctrl = serdev->ctrl;
+
+     if (!ctrl || !ctrl->ops->set_parity)
+             return;
+
+     ctrl->ops->set_parity(ctrl, enable, odd);
+}
+EXPORT_SYMBOL_GPL(serdev_device_set_parity);
+

this really needs Rob?s ACK before I take the patch.

sure

I could even live with a NACK in case these two bool parameters are considered to be ugly
in that case I would propose an enum with three values: DISABLED, EVEN, ODD so the arguments would look like this:
void serdev_device_set_parity(struct serdev_device *serdev, enum parity)

Regards
Martin

author reviewer
#4 lore raw

RFC v2 1/9 serdev: implement parity configuration

Quoted 41 lines · Martin Blumenstingl

Hi Marcel, Hi Rob,

Quoted 24 lines · Marcel Holtmann

Hi Martin,

Quoted 22 lines

Some Bluetooth modules (for example the ones found in Realtek RTL8723BS and RTL8723DS) want to communicate with the host with even parity enabled.
Add a new function and the corresponding internal callbacks so parity can be configured. This supports enabling and disabling parity as well as setting the type to odd or even.

---

drivers/tty/serdev/core.c           | 12 ++++++++++++
drivers/tty/serdev/serdev-ttyport.c | 21 +++++++++++++++++++++
include/linux/serdev.h              |  5 +++++
3 files changed, 38 insertions(+)
drivers/tty/serdev/core.c +0 -0
index 1bef39828ca7..d327b02980f5 100644
@@ -225,6 +225,18 @@ void serdev_device_set_flow_control(struct serdev_device *serdev, bool enable)
 }
 EXPORT_SYMBOL_GPL(serdev_device_set_flow_control);
 
Quoted 14 lines

drivers/tty/serdev/core.c void serdev_device_set_flow_control(struct serdev_device *serdev, bool enable) · line 239

Quoted 12 lines
drivers/tty/serdev/core.c +12 -0
+void serdev_device_set_parity(struct serdev_device *serdev, bool enable,
+                           bool odd)
+{
+     struct serdev_controller *ctrl = serdev->ctrl;
+
+     if (!ctrl || !ctrl->ops->set_parity)
+             return;
+
+     ctrl->ops->set_parity(ctrl, enable, odd);
+}
+EXPORT_SYMBOL_GPL(serdev_device_set_parity);
+

this really needs Rob?s ACK before I take the patch.

Quoted 7 lines

sure

I could even live with a NACK in case these two bool parameters are considered to be ugly
in that case I would propose an enum with three values: DISABLED, EVEN, ODD so the arguments would look like this:
void serdev_device_set_parity(struct serdev_device *serdev, enum parity)

I just discovered: such a patch was already posted by Ulrich Hecht: [0]

[0] https://patchwork.kernel.org/patch/9903787/

#5 lore raw

RFC v2 1/9 serdev: implement parity configuration

Quoted 50 lines · Martin Blumenstingl
Quoted 41 lines · Martin Blumenstingl

Hi Marcel, Hi Rob,

Quoted 24 lines · Marcel Holtmann

Hi Martin,

Quoted 22 lines

Some Bluetooth modules (for example the ones found in Realtek RTL8723BS and RTL8723DS) want to communicate with the host with even parity enabled.
Add a new function and the corresponding internal callbacks so parity can be configured. This supports enabling and disabling parity as well as setting the type to odd or even.

---

drivers/tty/serdev/core.c           | 12 ++++++++++++
drivers/tty/serdev/serdev-ttyport.c | 21 +++++++++++++++++++++
include/linux/serdev.h              |  5 +++++
3 files changed, 38 insertions(+)
drivers/tty/serdev/core.c +0 -0
index 1bef39828ca7..d327b02980f5 100644
@@ -225,6 +225,18 @@ void serdev_device_set_flow_control(struct serdev_device *serdev, bool enable)
 }
 EXPORT_SYMBOL_GPL(serdev_device_set_flow_control);
 
Quoted 14 lines

drivers/tty/serdev/core.c void serdev_device_set_flow_control(struct serdev_device *serdev, bool enable) · line 239

Quoted 12 lines
drivers/tty/serdev/core.c +12 -0
+void serdev_device_set_parity(struct serdev_device *serdev, bool enable,
+                           bool odd)
+{
+     struct serdev_controller *ctrl = serdev->ctrl;
+
+     if (!ctrl || !ctrl->ops->set_parity)
+             return;
+
+     ctrl->ops->set_parity(ctrl, enable, odd);
+}
+EXPORT_SYMBOL_GPL(serdev_device_set_parity);
+

this really needs Rob?s ACK before I take the patch.

Quoted 7 lines

sure

I could even live with a NACK in case these two bool parameters are considered to be ugly
in that case I would propose an enum with three values: DISABLED, EVEN, ODD so the arguments would look like this:
void serdev_device_set_parity(struct serdev_device *serdev, enum parity)

Quoted 4 lines

I just discovered: such a patch was already posted by Ulrich Hecht: [0]

[0] https://patchwork.kernel.org/patch/9903787/

Yeah, that would be the preferred way of doing this as it's more readable and less error prone (and more easily extended if anyone ever needs mark and space parity).

Also, I know serdev currently fails to check for errors from tty_set_termios, but we really need to start doing that. Not all serial drivers support (every) parity mode so you need to check the tty termios for the actual mode used after tty_set_termios() returns.

Thanks,
Johan

maintainer
#6 lore raw

RFC v2 1/9 serdev: implement parity configuration

In reply to Martin Blumenstingl

Hi Martin,

Quoted 43 lines
Quoted 36 lines
Quoted 22 lines
Quoted 22 lines

Some Bluetooth modules (for example the ones found in Realtek RTL8723BS and RTL8723DS) want to communicate with the host with even parity enabled.
Add a new function and the corresponding internal callbacks so parity can be configured. This supports enabling and disabling parity as well as setting the type to odd or even.

---

drivers/tty/serdev/core.c           | 12 ++++++++++++
drivers/tty/serdev/serdev-ttyport.c | 21 +++++++++++++++++++++
include/linux/serdev.h              |  5 +++++
3 files changed, 38 insertions(+)
drivers/tty/serdev/core.c +0 -0
index 1bef39828ca7..d327b02980f5 100644
@@ -225,6 +225,18 @@ void serdev_device_set_flow_control(struct serdev_device *serdev, bool enable)
 }
 EXPORT_SYMBOL_GPL(serdev_device_set_flow_control);
 
Quoted 14 lines

drivers/tty/serdev/core.c void serdev_device_set_flow_control(struct serdev_device *serdev, bool enable) · line 239

Quoted 12 lines
drivers/tty/serdev/core.c +12 -0
+void serdev_device_set_parity(struct serdev_device *serdev, bool enable,
+                           bool odd)
+{
+     struct serdev_controller *ctrl = serdev->ctrl;
+
+     if (!ctrl || !ctrl->ops->set_parity)
+             return;
+
+     ctrl->ops->set_parity(ctrl, enable, odd);
+}
+EXPORT_SYMBOL_GPL(serdev_device_set_parity);
+

this really needs Rob?s ACK before I take the patch.

Quoted 7 lines

sure

I could even live with a NACK in case these two bool parameters are considered to be ugly
in that case I would propose an enum with three values: DISABLED, EVEN, ODD so the arguments would look like this:
void serdev_device_set_parity(struct serdev_device *serdev, enum parity)

Quoted 4 lines

I just discovered: such a patch was already posted by Ulrich Hecht: [0]

[0] https://patchwork.kernel.org/patch/9903787/

any idea what the status of this one is? It would be good if we get an ACK from Rob and you just include it in your patch series. I do not see it currently in Linus? tree or net-next. If it goes via a different path to Linus, we will have a bit of a problem getting this all merged for the next kernel.

Regards

Marcel

author reviewer
#7 lore raw

RFC v2 2/9 dt-bindings: net: bluetooth: add support for Realtek Bluetooth chips

In reply to Martin Blumenstingl

This adds the documentation for Bluetooth functionality of the Realtek RTL8723BS and RTL8723DS.
Both are SDIO wifi chips with an additional Bluetooth module which is connected via UART to the host.

---
 .../devicetree/bindings/net/realtek-bluetooth.txt  | 41 ++++++++++++++++++++++
 1 file changed, 41 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/net/realtek-bluetooth.txt
Documentation/devicetree/bindings/net/realtek-bluetooth.txt new file +41 -0
new file mode 100644
index 000000000000..1491329c4cd1
@@ -0,0 +1,41 @@
+Realtek Bluetooth Chips
+-----------------------
+
+This documents the binding structure and common properties for serial
+attached Realtek devices.
+
+Serial attached Realtek devices shall be a child node of the host UART
+device the slave device is attached to. See ../serial/slave-device.txt
+for more information
+
+Required properties:
+- compatible: should contain one of the following:
+    * "realtek,rtl8723bs-bluetooth"
+    * "realtek,rtl8723ds-bluetooth"
+
+Optional properties:
+- realtek,config-data: Bluetooth chipset configuration data which is
+			needed for communication (it typically contains
+			board specific settings like the baudrate and
+			whether flow-control is used).
+			This is an array of u8 values.
+- enable-gpios: GPIO specifier, used to enable/disable the BT module
+- reset-gpios: GPIO specifier, used to reset the BT module
+
+
+Example:
+
+&uart {
+	...
+
+	bluetooth {
+		compatible = "realtek,rtl8723bs-bluetooth";
+		enable-gpios = <&gpio 20 GPIO_ACTIVE_HIGH>;
+		reset-gpios = <&gpio 11 GPIO_ACTIVE_HIGH>;
+		realtek,config-data = /bits/ 8 <
+			0x55 0xab 0x23 0x87 0x29 0x00 0xf4 0x00 0x01 0x01 0xf6 0x00
+			0x02 0x81 0x00 0xfa 0x00 0x02 0x12 0x80 0x0c 0x00 0x10 0x02
+			0x80 0x92 0x04 0x50 0xc5 0xea 0x19 0xe1 0x1b 0xfd 0xaf 0x5f
+			0x01 0xa4 0x0b 0xd9 0x00 0x01 0x0f 0xe4 0x00 0x01 0x08>;
+	};
+};
Signature · 1 line
2.15.1
maintainer
#8 lore raw

RFC v2 2/9 dt-bindings: net: bluetooth: add support for Realtek Bluetooth chips

Hi Martin,

Quoted 28 lines

This adds the documentation for Bluetooth functionality of the Realtek RTL8723BS and RTL8723DS.
Both are SDIO wifi chips with an additional Bluetooth module which is connected via UART to the host.

---
.../devicetree/bindings/net/realtek-bluetooth.txt | 41 ++++++++++++++++++++++

1 file changed, 41 insertions(+)

create mode 100644 Documentation/devicetree/bindings/net/realtek-bluetooth.txt

Documentation/devicetree/bindings/net/realtek-bluetooth.txt new file +11 -0
new file mode 100644
index 000000000000..1491329c4cd1
@@ -0,0 +1,41 @@
+Realtek Bluetooth Chips
+-----------------------
+
+This documents the binding structure and common properties for serial
+attached Realtek devices.
+
+Serial attached Realtek devices shall be a child node of the host UART
+device the slave device is attached to. See ../serial/slave-device.txt
+for more information
+
+Required properties:

Documentation/devicetree/bindings/net/realtek-bluetooth.txt line 21

Quoted 10 lines
Documentation/devicetree/bindings/net/realtek-bluetooth.txt new file +10 -0
+- compatible: should contain one of the following:
+    * "realtek,rtl8723bs-bluetooth"
+    * "realtek,rtl8723ds-bluetooth"
+
+Optional properties:
+- realtek,config-data: Bluetooth chipset configuration data which is
+			needed for communication (it typically contains
+			board specific settings like the baudrate and
+			whether flow-control is used).
+			This is an array of u8 values.

any chance we can at least include the basic format of these config blobs. And I prefer at least an ACK from Rob here.

Quoted 10 lines
Documentation/devicetree/bindings/net/realtek-bluetooth.txt new file +10 -0
+- enable-gpios: GPIO specifier, used to enable/disable the BT module
+- reset-gpios: GPIO specifier, used to reset the BT module
+
+
+Example:
+
+&uart {
+	...
+
+	bluetooth {

Documentation/devicetree/bindings/net/realtek-bluetooth.txt line 41

Quoted 10 lines
Documentation/devicetree/bindings/net/realtek-bluetooth.txt new file +10 -0
+		compatible = "realtek,rtl8723bs-bluetooth";
+		enable-gpios = <&gpio 20 GPIO_ACTIVE_HIGH>;
+		reset-gpios = <&gpio 11 GPIO_ACTIVE_HIGH>;
+		realtek,config-data = /bits/ 8 <
+			0x55 0xab 0x23 0x87 0x29 0x00 0xf4 0x00 0x01 0x01 0xf6 0x00
+			0x02 0x81 0x00 0xfa 0x00 0x02 0x12 0x80 0x0c 0x00 0x10 0x02
+			0x80 0x92 0x04 0x50 0xc5 0xea 0x19 0xe1 0x1b 0xfd 0xaf 0x5f
+			0x01 0xa4 0x0b 0xd9 0x00 0x01 0x0f 0xe4 0x00 0x01 0x08>;
+	};
+};

Regards

Marcel

author reviewer
#9 lore raw

RFC v2 2/9 dt-bindings: net: bluetooth: add support for Realtek Bluetooth chips

Hi Marcel,

Quoted 34 lines · Marcel Holtmann

Hi Martin,

Quoted 32 lines

This adds the documentation for Bluetooth functionality of the Realtek RTL8723BS and RTL8723DS.
Both are SDIO wifi chips with an additional Bluetooth module which is connected via UART to the host.

---
.../devicetree/bindings/net/realtek-bluetooth.txt | 41 ++++++++++++++++++++++

1 file changed, 41 insertions(+)

create mode 100644 Documentation/devicetree/bindings/net/realtek-bluetooth.txt

Documentation/devicetree/bindings/net/realtek-bluetooth.txt new file +15 -0
new file mode 100644
index 000000000000..1491329c4cd1
@@ -0,0 +1,41 @@
+Realtek Bluetooth Chips
+-----------------------
+
+This documents the binding structure and common properties for serial
+attached Realtek devices.
+
+Serial attached Realtek devices shall be a child node of the host UART
+device the slave device is attached to. See ../serial/slave-device.txt
+for more information
+
+Required properties:
+- compatible: should contain one of the following:
+    * "realtek,rtl8723bs-bluetooth"
+    * "realtek,rtl8723ds-bluetooth"
+
Quoted 8 lines

Documentation/devicetree/bindings/net/realtek-bluetooth.txt line 21

Quoted 6 lines
Documentation/devicetree/bindings/net/realtek-bluetooth.txt new file +6 -0
+Optional properties:
+- realtek,config-data: Bluetooth chipset configuration data which is
+                     needed for communication (it typically contains
+                     board specific settings like the baudrate and
+                     whether flow-control is used).
+                     This is an array of u8 values.

any chance we can at least include the basic format of these config blobs. And I prefer at least an ACK from Rob here.

with including the basic format you mean a description that the config blob should start with 0x55 0xab 0x23 0x87 (which translates to:
0x8723ab55)?

I think all non-trivial dt-binding patches should be ACKed by the DT maintainers, so waiting for Rob's ACK is perfectly fine for me

Quoted 12 lines
Quoted 12 lines
Documentation/devicetree/bindings/net/realtek-bluetooth.txt new file +12 -0
+- enable-gpios: GPIO specifier, used to enable/disable the BT module
+- reset-gpios: GPIO specifier, used to reset the BT module
+
+
+Example:
+
+&uart {
+     ...
+
+     bluetooth {
+             compatible = "realtek,rtl8723bs-bluetooth";
+             enable-gpios = <&gpio 20 GPIO_ACTIVE_HIGH>;
Quoted 8 lines

Documentation/devicetree/bindings/net/realtek-bluetooth.txt line 41

Quoted 8 lines
Documentation/devicetree/bindings/net/realtek-bluetooth.txt new file +8 -0
+             reset-gpios = <&gpio 11 GPIO_ACTIVE_HIGH>;
+             realtek,config-data = /bits/ 8 <
+                     0x55 0xab 0x23 0x87 0x29 0x00 0xf4 0x00 0x01 0x01 0xf6 0x00
+                     0x02 0x81 0x00 0xfa 0x00 0x02 0x12 0x80 0x0c 0x00 0x10 0x02
+                     0x80 0x92 0x04 0x50 0xc5 0xea 0x19 0xe1 0x1b 0xfd 0xaf 0x5f
+                     0x01 0xa4 0x0b 0xd9 0x00 0x01 0x0f 0xe4 0x00 0x01 0x08>;
+     };
+};

Regards
Martin

#10 lore raw

RFC v2 2/9 dt-bindings: net: bluetooth: add support for Realtek Bluetooth chips

Quoted 45 lines · Martin Blumenstingl

Hi Marcel,

Quoted 34 lines · Marcel Holtmann

Hi Martin,

Quoted 32 lines

This adds the documentation for Bluetooth functionality of the Realtek RTL8723BS and RTL8723DS.
Both are SDIO wifi chips with an additional Bluetooth module which is connected via UART to the host.

---
.../devicetree/bindings/net/realtek-bluetooth.txt | 41 ++++++++++++++++++++++

1 file changed, 41 insertions(+)

create mode 100644 Documentation/devicetree/bindings/net/realtek-bluetooth.txt

Documentation/devicetree/bindings/net/realtek-bluetooth.txt new file +15 -0
new file mode 100644
index 000000000000..1491329c4cd1
@@ -0,0 +1,41 @@
+Realtek Bluetooth Chips
+-----------------------
+
+This documents the binding structure and common properties for serial
+attached Realtek devices.
+
+Serial attached Realtek devices shall be a child node of the host UART
+device the slave device is attached to. See ../serial/slave-device.txt
+for more information
+
+Required properties:
+- compatible: should contain one of the following:
+    * "realtek,rtl8723bs-bluetooth"
+    * "realtek,rtl8723ds-bluetooth"
+
Quoted 8 lines

Documentation/devicetree/bindings/net/realtek-bluetooth.txt line 21

Quoted 6 lines
Documentation/devicetree/bindings/net/realtek-bluetooth.txt new file +6 -0
+Optional properties:
+- realtek,config-data: Bluetooth chipset configuration data which is
+                     needed for communication (it typically contains
+                     board specific settings like the baudrate and
+                     whether flow-control is used).
+                     This is an array of u8 values.

any chance we can at least include the basic format of these config blobs. And I prefer at least an ACK from Rob here.

Quoted 3 lines

with including the basic format you mean a description that the config blob should start with 0x55 0xab 0x23 0x87 (which translates to:
0x8723ab55)?

That and the size if it is fixed (or fixed per compatible). Also, would be useful to state what are the defaults if not present.

If it is all documented somewhere else, a pointer to that is fine.

Quoted 5 lines

I think all non-trivial dt-binding patches should be ACKed by the DT maintainers, so waiting for Rob's ACK is perfectly fine for me

Quoted 2 lines
Quoted 2 lines
Documentation/devicetree/bindings/net/realtek-bluetooth.txt new file +2 -0
+- enable-gpios: GPIO specifier, used to enable/disable the BT module
+- reset-gpios: GPIO specifier, used to reset the BT module

These should state active high or low.

Rob

author reviewer
#11 lore raw

RFC v2 3/9 Bluetooth: btrtl: add MODULE_FIRMWARE declarations

In reply to Martin Blumenstingl

This makes the firmware names show up in modinfo.

---
 drivers/bluetooth/btrtl.c | 9 +++++++++
 1 file changed, 9 insertions(+)
drivers/bluetooth/btrtl.c +9 -0
index 6e2ad748abba..3e5296287808 100644
@@ -477,3 +477,12 @@ MODULE_AUTHOR("Daniel Drake <[email protected]>");
 MODULE_DESCRIPTION("Bluetooth support for Realtek devices ver " VERSION);
 MODULE_VERSION(VERSION);
 MODULE_LICENSE("GPL");
+MODULE_FIRMWARE("rtl_bt/rtl8723a_fw.bin");
+MODULE_FIRMWARE("rtl_bt/rtl8723b_fw.bin");
+MODULE_FIRMWARE("rtl_bt/rtl8723b_config.bin");
+MODULE_FIRMWARE("rtl_bt/rtl8761a_fw.bin");
+MODULE_FIRMWARE("rtl_bt/rtl8761a_config.bin");
+MODULE_FIRMWARE("rtl_bt/rtl8821a_fw.bin");
+MODULE_FIRMWARE("rtl_bt/rtl8821a_config.bin");
+MODULE_FIRMWARE("rtl_bt/rtl8822b_fw.bin");
+MODULE_FIRMWARE("rtl_bt/rtl8822b_config.bin");
Signature · 1 line
2.15.1
author reviewer
#12 lore raw

RFC v2 4/9 Bluetooth: btrtl: split the device initialization into smaller parts

In reply to Martin Blumenstingl

This prepares the btrtl code so it can be used to initialize Bluetooth modules connected via UART (these are found for example on the RTL8723BS and RTL8723DS SDIO chips, which come with an embedded UART Bluetooth module).

The Realtek "rtl8723bs_bt" and "rtl8723ds_bt" userspace Bluetooth UART initialization tools (rtk_hciattach) use the following sequence:
1) send H5 sync pattern (already supported by hci_h5)
2) get LMP version (already supported by btrtl)
3) get ROM version (already supported by btrtl)
4) load the firmware and config for the current chipset (already supported by btrtl)
5) read UART settings from the config blob (currently not supported)
6) send UART settings via a vendor command to the device (which changes the baudrate of the device and enables or disables flow control depending on the config)
7) change the baudrate and flow control settings on the host
8) send the firmware and config blob to the device (already supported by btrtl)

The main reason why the initialization has to be split is step #7. This requires changes to the underlying "bus", which should be kept outside of the "generic" btrtl driver.
The idea for this split is borrowed from the btbcm driver but adjusted where needed (the btrtl driver for example needs two blobs: firmware and config, while the btbcm only needs one).

This also prepares the code for step #5 (parsing the config blob) by centralizing the code which loads the firmware and config blobs and storing the result in the new struct btrtl_device_info.

---
 drivers/bluetooth/btrtl.c | 273 +++++++++++++++++++++++++++-------------------
 drivers/bluetooth/btrtl.h |  21 ++++
 2 files changed, 180 insertions(+), 114 deletions(-)
drivers/bluetooth/btrtl.c +159 -114
index 3e5296287808..300ee15875e6 100644
@@ -35,6 +35,15 @@
 #define RTL_ROM_LMP_8761A	0x8761
 #define RTL_ROM_LMP_8822B	0x8822
 
+struct btrtl_device_info {
+	u16 lmp_subver;
+	u8 rom_version;
+	u8 *fw_data;
+	int fw_len;
+	u8 *cfg_data;
+	int cfg_len;
+};
+
 static int rtl_read_rom_version(struct hci_dev *hdev, u8 *version)
 {
 	struct rtl_rom_version_evt *rom_version;
@@ -64,16 +73,16 @@ static int rtl_read_rom_version(struct hci_dev *hdev, u8 *version)
 	return 0;
 }
 
-static int rtl8723b_parse_firmware(struct hci_dev *hdev, u16 lmp_subver,
-				   const struct firmware *fw,
+static int rtl8723b_parse_firmware(struct hci_dev *hdev,
+				   struct btrtl_device_info *btrtl_dev,
 				   unsigned char **_buf)
 {
 	const u8 extension_sig[] = { 0x51, 0x04, 0xfd, 0x77 };
 	struct rtl_epatch_header *epatch_info;
 	unsigned char *buf;
-	int i, ret, len;
+	int i, len;
 	size_t min_size;
-	u8 opcode, length, data, rom_version = 0;
+	u8 opcode, length, data;
 	int project_id = -1;
 	const unsigned char *fwptr, *chip_id_base;
 	const unsigned char *patch_length_base, *patch_offset_base;
@@ -90,15 +99,11 @@ static int rtl8723b_parse_firmware(struct hci_dev *hdev, u16 lmp_subver,
 		{ RTL_ROM_LMP_8822B, 8 },
 	};
 
-	ret = rtl_read_rom_version(hdev, &rom_version);
-	if (ret)
-		return ret;
-
 	min_size = sizeof(struct rtl_epatch_header) + sizeof(extension_sig) + 3;
-	if (fw->size < min_size)
+	if (btrtl_dev->fw_len < min_size)
 		return -EINVAL;
 
-	fwptr = fw->data + fw->size - sizeof(extension_sig);
+	fwptr = btrtl_dev->fw_data + btrtl_dev->fw_len - sizeof(extension_sig);
 	if (memcmp(fwptr, extension_sig, sizeof(extension_sig)) != 0) {
 		BT_ERR("%s: extension section signature mismatch", hdev->name);
 		return -EINVAL;
@@ -110,7 +115,7 @@ static int rtl8723b_parse_firmware(struct hci_dev *hdev, u16 lmp_subver,
 	 * Once we have that, we double-check that that project_id is suitable
 	 * for the hardware we are working with.
 	 */
-	while (fwptr >= fw->data + (sizeof(struct rtl_epatch_header) + 3)) {
+	while (fwptr >= btrtl_dev->fw_data + (sizeof(*epatch_info) + 3)) {
 		opcode = *--fwptr;
 		length = *--fwptr;
 		data = *--fwptr;
@@ -150,13 +155,14 @@ static int rtl8723b_parse_firmware(struct hci_dev *hdev, u16 lmp_subver,
 		return -EINVAL;
 	}
 
-	if (lmp_subver != project_id_to_lmp_subver[i].lmp_subver) {
+	if (btrtl_dev->lmp_subver != project_id_to_lmp_subver[i].lmp_subver) {
 		BT_ERR("%s: firmware is for %x but this is a %x", hdev->name,
-		       project_id_to_lmp_subver[i].lmp_subver, lmp_subver);
+		       project_id_to_lmp_subver[i].lmp_subver,
+		       btrtl_dev->lmp_subver);
 		return -EINVAL;
 	}
 
-	epatch_info = (struct rtl_epatch_header *)fw->data;
+	epatch_info = (struct rtl_epatch_header *)btrtl_dev->fw_data;
 	if (memcmp(epatch_info->signature, RTL_EPATCH_SIGNATURE, 8) != 0) {
 		BT_ERR("%s: bad EPATCH signature", hdev->name);
 		return -EINVAL;
@@ -173,16 +179,16 @@ static int rtl8723b_parse_firmware(struct hci_dev *hdev, u16 lmp_subver,
 	 * Find the right patch for this chip.
 	 */
 	min_size += 8 * num_patches;
-	if (fw->size < min_size)
+	if (btrtl_dev->fw_len < min_size)
 		return -EINVAL;
 
-	chip_id_base = fw->data + sizeof(struct rtl_epatch_header);
+	chip_id_base = btrtl_dev->fw_data + sizeof(struct rtl_epatch_header);
 	patch_length_base = chip_id_base + (sizeof(u16) * num_patches);
 	patch_offset_base = patch_length_base + (sizeof(u16) * num_patches);
 	for (i = 0; i < num_patches; i++) {
 		u16 chip_id = get_unaligned_le16(chip_id_base +
 						 (i * sizeof(u16)));
-		if (chip_id == rom_version + 1) {
+		if (chip_id == btrtl_dev->rom_version + 1) {
 			patch_length = get_unaligned_le16(patch_length_base +
 							  (i * sizeof(u16)));
 			patch_offset = get_unaligned_le32(patch_offset_base +
@@ -193,20 +199,21 @@ static int rtl8723b_parse_firmware(struct hci_dev *hdev, u16 lmp_subver,
 
 	if (!patch_offset) {
 		BT_ERR("%s: didn't find patch for chip id %d",
-		       hdev->name, rom_version);
+		       hdev->name, btrtl_dev->rom_version);
 		return -EINVAL;
 	}
 
 	BT_DBG("length=%x offset=%x index %d", patch_length, patch_offset, i);
 	min_size = patch_offset + patch_length;
-	if (fw->size < min_size)
+	if (btrtl_dev->fw_len < min_size)
 		return -EINVAL;
 
 	/* Copy the firmware into a new buffer and write the version at
 	 * the end.
 	 */
 	len = patch_length;
-	buf = kmemdup(fw->data + patch_offset, patch_length, GFP_KERNEL);
+	buf = kmemdup(btrtl_dev->fw_data + patch_offset, patch_length,
+		      GFP_KERNEL);
 	if (!buf)
 		return -ENOMEM;
 
@@ -268,7 +275,7 @@ static int rtl_download_firmware(struct hci_dev *hdev,
 	return ret;
 }
 
-static int rtl_load_config(struct hci_dev *hdev, const char *name, u8 **buff)
+static int rtl_load_file(struct hci_dev *hdev, const char *name, u8 **buff)
 {
 	const struct firmware *fw;
 	int ret;
@@ -287,95 +294,37 @@ static int rtl_load_config(struct hci_dev *hdev, const char *name, u8 **buff)
 	return ret;
 }
 
-static int btrtl_setup_rtl8723a(struct hci_dev *hdev)
+static int btrtl_setup_rtl8723a(struct hci_dev *hdev,
+				struct btrtl_device_info *btrtl_dev)
 {
-	const struct firmware *fw;
-	int ret;
-
-	bt_dev_info(hdev, "rtl: loading rtl_bt/rtl8723a_fw.bin");
-	ret = request_firmware(&fw, "rtl_bt/rtl8723a_fw.bin", &hdev->dev);
-	if (ret < 0) {
-		BT_ERR("%s: Failed to load rtl_bt/rtl8723a_fw.bin", hdev->name);
-		return ret;
-	}
-
-	if (fw->size < 8) {
-		ret = -EINVAL;
-		goto out;
-	}
+	if (btrtl_dev->fw_len < 8)
+		return -EINVAL;
 
 	/* Check that the firmware doesn't have the epatch signature
 	 * (which is only for RTL8723B and newer).
 	 */
-	if (!memcmp(fw->data, RTL_EPATCH_SIGNATURE, 8)) {
+	if (!memcmp(btrtl_dev->fw_data, RTL_EPATCH_SIGNATURE, 8)) {
 		BT_ERR("%s: unexpected EPATCH signature!", hdev->name);
-		ret = -EINVAL;
-		goto out;
+		return -EINVAL;
 	}
 
-	ret = rtl_download_firmware(hdev, fw->data, fw->size);
-
-out:
-	release_firmware(fw);
-	return ret;
+	return rtl_download_firmware(hdev, btrtl_dev->fw_data,
+				     btrtl_dev->fw_len);
 }
 
-static int btrtl_setup_rtl8723b(struct hci_dev *hdev, u16 lmp_subver,
-				const char *fw_name)
+static int btrtl_setup_rtl8723b(struct hci_dev *hdev,
+				struct btrtl_device_info *btrtl_dev)
 {
 	unsigned char *fw_data = NULL;
-	const struct firmware *fw;
 	int ret;
-	int cfg_sz;
-	u8 *cfg_buff = NULL;
 	u8 *tbuff;
-	char *cfg_name = NULL;
-	bool config_needed = false;
 
-	switch (lmp_subver) {
-	case RTL_ROM_LMP_8723B:
-		cfg_name = "rtl_bt/rtl8723b_config.bin";
-		break;
-	case RTL_ROM_LMP_8821A:
-		cfg_name = "rtl_bt/rtl8821a_config.bin";
-		break;
-	case RTL_ROM_LMP_8761A:
-		cfg_name = "rtl_bt/rtl8761a_config.bin";
-		break;
-	case RTL_ROM_LMP_8822B:
-		cfg_name = "rtl_bt/rtl8822b_config.bin";
-		config_needed = true;
-		break;
-	default:
-		BT_ERR("%s: rtl: no config according to lmp_subver %04x",
-		       hdev->name, lmp_subver);
-		break;
-	}
-
-	if (cfg_name) {
-		cfg_sz = rtl_load_config(hdev, cfg_name, &cfg_buff);
-		if (cfg_sz < 0) {
-			cfg_sz = 0;
-			if (config_needed)
-				BT_ERR("Necessary config file %s not found\n",
-				       cfg_name);
-		}
-	} else
-		cfg_sz = 0;
-
-	bt_dev_info(hdev, "rtl: loading %s", fw_name);
-	ret = request_firmware(&fw, fw_name, &hdev->dev);
-	if (ret < 0) {
-		BT_ERR("%s: Failed to load %s", hdev->name, fw_name);
-		goto err_req_fw;
-	}
-
-	ret = rtl8723b_parse_firmware(hdev, lmp_subver, fw, &fw_data);
+	ret = rtl8723b_parse_firmware(hdev, btrtl_dev, &fw_data);
 	if (ret < 0)
 		goto out;
 
-	if (cfg_sz) {
-		tbuff = kzalloc(ret + cfg_sz, GFP_KERNEL);
+	if (btrtl_dev->cfg_len > 0) {
+		tbuff = kzalloc(ret + btrtl_dev->cfg_len, GFP_KERNEL);
 		if (!tbuff) {
 			ret = -ENOMEM;
 			goto out;
@@ -384,22 +333,18 @@ static int btrtl_setup_rtl8723b(struct hci_dev *hdev, u16 lmp_subver,
 		memcpy(tbuff, fw_data, ret);
 		kfree(fw_data);
 
-		memcpy(tbuff + ret, cfg_buff, cfg_sz);
-		ret += cfg_sz;
+		memcpy(tbuff + ret, btrtl_dev->cfg_data, btrtl_dev->cfg_len);
+		ret += btrtl_dev->cfg_len;
 
 		fw_data = tbuff;
 	}
 
-	bt_dev_info(hdev, "cfg_sz %d, total size %d", cfg_sz, ret);
+	bt_dev_info(hdev, "cfg_sz %d, total size %d", btrtl_dev->cfg_len, ret);
 
 	ret = rtl_download_firmware(hdev, fw_data, ret);
 
 out:
-	release_firmware(fw);
 	kfree(fw_data);
-err_req_fw:
-	if (cfg_sz)
-		kfree(cfg_buff);
 	return ret;
 }
 
@@ -425,15 +370,34 @@ static struct sk_buff *btrtl_read_local_version(struct hci_dev *hdev)
 	return skb;
 }
 
-int btrtl_setup_realtek(struct hci_dev *hdev)
+void btrtl_free(struct btrtl_device_info *btrtl_dev)
 {
+	kfree(btrtl_dev->fw_data);
+	kfree(btrtl_dev->cfg_data);
+	kfree(btrtl_dev);
+}
+EXPORT_SYMBOL_GPL(btrtl_free);
+
+struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev)
+{
+	struct btrtl_device_info *btrtl_dev;
 	struct sk_buff *skb;
 	struct hci_rp_read_local_version *resp;
-	u16 lmp_subver;
+	bool cfg_needed = false, has_rom_version;
+	const char *cfg_name, *fw_name;
+	int ret;
+
+	btrtl_dev = kzalloc(sizeof(*btrtl_dev), GFP_KERNEL);
+	if (!btrtl_dev) {
+		ret = -ENOMEM;
+		goto err_alloc;
+	}
 
 	skb = btrtl_read_local_version(hdev);
-	if (IS_ERR(skb))
-		return -PTR_ERR(skb);
+	if (IS_ERR(skb)) {
+		ret = -PTR_ERR(skb);
+		goto err_free;
+	}
 
 	resp = (struct hci_rp_read_local_version *)skb->data;
 	bt_dev_info(hdev, "rtl: examining hci_ver=%02x hci_rev=%04x "
@@ -441,36 +405,117 @@ int btrtl_setup_realtek(struct hci_dev *hdev)
 		    resp->hci_ver, resp->hci_rev,
 		    resp->lmp_ver, resp->lmp_subver);
 
-	lmp_subver = le16_to_cpu(resp->lmp_subver);
+	btrtl_dev->lmp_subver = le16_to_cpu(resp->lmp_subver);
+
 	kfree_skb(skb);
 
+	switch (btrtl_dev->lmp_subver) {
+	case RTL_ROM_LMP_8723A:
+	case RTL_ROM_LMP_3499:
+		fw_name = "rtl_bt/rtl8723a_fw.bin";
+		cfg_name = NULL;
+		has_rom_version = false;
+		break;
+	case RTL_ROM_LMP_8723B:
+		fw_name = "rtl_bt/rtl8723b_fw.bin";
+		cfg_name = "rtl_bt/rtl8723b_config.bin";
+		has_rom_version = true;
+		break;
+	case RTL_ROM_LMP_8821A:
+		fw_name = "rtl_bt/rtl8821a_fw.bin";
+		cfg_name = "rtl_bt/rtl8821a_config.bin";
+		has_rom_version = true;
+		break;
+	case RTL_ROM_LMP_8761A:
+		fw_name = "rtl_bt/rtl8761a_fw.bin";
+		cfg_name = "rtl_bt/rtl8761a_config.bin";
+		has_rom_version = true;
+		break;
+	case RTL_ROM_LMP_8822B:
+		fw_name = "rtl_bt/rtl8822b_fw.bin";
+		cfg_name = "rtl_bt/rtl8822b_config.bin";
+		has_rom_version = true;
+		cfg_needed = true;
+		break;
+	default:
+		bt_dev_info(hdev, "rtl: assuming no firmware upload needed");
+		return btrtl_dev;
+	}
+
+	if (has_rom_version) {
+		ret = rtl_read_rom_version(hdev, &btrtl_dev->rom_version);
+		if (ret)
+			goto err_free;
+	}
+
+	btrtl_dev->fw_len = rtl_load_file(hdev, fw_name, &btrtl_dev->fw_data);
+	if (btrtl_dev->fw_len < 0) {
+		bt_dev_err(hdev, "firmware file %s not found\n", fw_name);
+		ret = btrtl_dev->fw_len;
+		goto err_free;
+	}
+
+	if (cfg_name) {
+		btrtl_dev->cfg_len = rtl_load_file(hdev, cfg_name,
+						   &btrtl_dev->cfg_data);
+		if (cfg_needed && btrtl_dev->cfg_len <= 0) {
+			bt_dev_err(hdev,
+				   "mandatory config file %s not found\n",
+				   cfg_name);
+			ret = btrtl_dev->fw_len;
+			goto err_free;
+		}
+	}
+
+	return btrtl_dev;
+
+err_free:
+	btrtl_free(btrtl_dev);
+err_alloc:
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(btrtl_initialize);
+
+int btrtl_download_firmware(struct hci_dev *hdev,
+			    struct btrtl_device_info *btrtl_dev)
+{
 	/* Match a set of subver values that correspond to stock firmware,
 	 * which is not compatible with standard btusb.
 	 * If matched, upload an alternative firmware that does conform to
 	 * standard btusb. Once that firmware is uploaded, the subver changes
 	 * to a different value.
 	 */
-	switch (lmp_subver) {
+	switch (btrtl_dev->lmp_subver) {
 	case RTL_ROM_LMP_8723A:
 	case RTL_ROM_LMP_3499:
-		return btrtl_setup_rtl8723a(hdev);
+		return btrtl_setup_rtl8723a(hdev, btrtl_dev);
 	case RTL_ROM_LMP_8723B:
-		return btrtl_setup_rtl8723b(hdev, lmp_subver,
-					    "rtl_bt/rtl8723b_fw.bin");
 	case RTL_ROM_LMP_8821A:
-		return btrtl_setup_rtl8723b(hdev, lmp_subver,
-					    "rtl_bt/rtl8821a_fw.bin");
 	case RTL_ROM_LMP_8761A:
-		return btrtl_setup_rtl8723b(hdev, lmp_subver,
-					    "rtl_bt/rtl8761a_fw.bin");
 	case RTL_ROM_LMP_8822B:
-		return btrtl_setup_rtl8723b(hdev, lmp_subver,
-					    "rtl_bt/rtl8822b_fw.bin");
+		return btrtl_setup_rtl8723b(hdev, btrtl_dev);
 	default:
 		bt_dev_info(hdev, "rtl: assuming no firmware upload needed");
 		return 0;
 	}
 }
+EXPORT_SYMBOL_GPL(btrtl_download_firmware);
+
+int btrtl_setup_realtek(struct hci_dev *hdev)
+{
+	struct btrtl_device_info *btrtl_dev;
+	int ret;
+
+	btrtl_dev = btrtl_initialize(hdev);
+	if (IS_ERR(btrtl_dev))
+		return PTR_ERR(btrtl_dev);
+
+	ret = btrtl_download_firmware(hdev, btrtl_dev);
+
+	btrtl_free(btrtl_dev);
+
+	return ret;
+}
 EXPORT_SYMBOL_GPL(btrtl_setup_realtek);
 
 MODULE_AUTHOR("Daniel Drake <[email protected]>");
drivers/bluetooth/btrtl.h +21 -0
index 38ffe4890cd1..21c28dcbe5e0 100644
@@ -17,6 +17,8 @@
 
 #define RTL_FRAG_LEN 252
 
+struct btrtl_device_info;
+
 struct rtl_download_cmd {
 	__u8 index;
 	__u8 data[RTL_FRAG_LEN];
@@ -40,10 +42,29 @@ struct rtl_epatch_header {
 
 #if IS_ENABLED(CONFIG_BT_RTL)
 
+struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev);
+void btrtl_free(struct btrtl_device_info *btrtl_dev);
+int btrtl_download_firmware(struct hci_dev *hdev,
+			    struct btrtl_device_info *btrtl_dev);
 int btrtl_setup_realtek(struct hci_dev *hdev);
 
 #else
 
+static inline struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev)
+{
+	return ERR_PTR(-EOPNOTSUPP);
+}
+
+static inline void btrtl_free(struct btrtl_device_info *btrtl_dev)
+{
+}
+
+static inline int btrtl_download_firmware(struct hci_dev *hdev,
+					  struct btrtl_device_info *btrtl_dev)
+{
+	return -EOPNOTSUPP;
+}
+
 static inline int btrtl_setup_realtek(struct hci_dev *hdev)
 {
 	return -EOPNOTSUPP;
Signature · 1 line
2.15.1
author reviewer
#13 lore raw

RFC v2 5/9 Bluetooth: btrtl: add support for retrieving the UART settings

In reply to Martin Blumenstingl

The UART settings are embedded in the config blob. This has to be parsed to successfully initialize the Bluetooth part of the RTL8723BS (which is an SDIO chip, but the Bluetooth part is connected via UART).

The Realtek "rtl8723bs_bt" and "rtl8723ds_bt" userspace Bluetooth UART initialization tools (rtk_hciattach) use the following sequence:
- send H5 sync pattern (already supported by hci_h5)
- get LMP version (already supported by btrtl)
- get ROM version (already supported by btrtl)
- load the firmware and config for the current chipset (already supported by btrtl)
- read UART settings from the config blob (part of this patch)
- send UART settings via a vendor command to the device (which changes the baudrate of the device and enables or disables flow control depending on the config)
- change the baudrate and flow control settings on the host
- send the firmware and config blob to the device (already supported by btrtl)

Sending the last firmware and config blob download command (rtl_download_cmd) fails if the UART settings are not updated beforehand. This is presumably because the device applies the config right after the firmware and config blob download - which means that at this point the host is using different UART settings than the device (which will obviously result in non-working communication).

---
 drivers/bluetooth/btrtl.c | 112 ++++++++++++++++++++++++++++++++++++++++++++++
 drivers/bluetooth/btrtl.h |  25 +++++++++++
 2 files changed, 137 insertions(+)
drivers/bluetooth/btrtl.c +112 -0
index 300ee15875e6..b6e8da4b73f8 100644
@@ -34,6 +34,7 @@
 #define RTL_ROM_LMP_8821A	0x8821
 #define RTL_ROM_LMP_8761A	0x8761
 #define RTL_ROM_LMP_8822B	0x8822
+#define RTL_CONFIG_MAGIC	0x8723ab55
 
 struct btrtl_device_info {
 	u16 lmp_subver;
@@ -518,6 +519,117 @@ int btrtl_setup_realtek(struct hci_dev *hdev)
 }
 EXPORT_SYMBOL_GPL(btrtl_setup_realtek);
 
+unsigned int btrtl_convert_baudrate(u32 device_baudrate)
+{
+	switch (device_baudrate) {
+	case 0x0252a00a:
+		return 230400;
+
+	case 0x05f75004:
+		return 921600;
+
+	case 0x00005004:
+		return 1000000;
+
+	case 0x04928002:
+	case 0x01128002:
+		return 1500000;
+
+	case 0x00005002:
+		return 2000000;
+
+	case 0x0000b001:
+		return 2500000;
+
+	case 0x04928001:
+		return 3000000;
+
+	case 0x052a6001:
+		return 3500000;
+
+	case 0x00005001:
+		return 4000000;
+
+	case 0x0252c014:
+	default:
+		return 115200;
+	}
+}
+
+int btrtl_get_uart_settings(struct hci_dev *hdev,
+			    struct btrtl_device_info *btrtl_dev,
+			    unsigned int *controller_baudrate,
+			    u32 *device_baudrate, bool *flow_control)
+{
+	struct rtl_vendor_config *config;
+	struct rtl_vendor_config_entry *entry;
+	int i, total_data_len;
+	bool found = false;
+
+	total_data_len = btrtl_dev->cfg_len - sizeof(*config);
+	if (total_data_len <= 0) {
+		bt_dev_warn(hdev, "rtl: no config loaded");
+		return -EINVAL;
+	}
+
+	config = (struct rtl_vendor_config *)btrtl_dev->cfg_data;
+	if (le32_to_cpu(config->signature) != RTL_CONFIG_MAGIC) {
+		bt_dev_err(hdev, "rtl: invalid config magic");
+		return -EINVAL;
+	}
+
+	if (total_data_len < le16_to_cpu(config->total_len)) {
+		bt_dev_err(hdev, "rtl: config is too short");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < total_data_len; ) {
+		entry = ((void *)config->entry) + i;
+
+		switch (le16_to_cpu(entry->offset)) {
+		case 0xc:
+			if (entry->len < sizeof(*device_baudrate)) {
+				bt_dev_err(hdev,
+					   "rtl: invalid UART config entry");
+				return -EINVAL;
+			}
+
+			*device_baudrate = get_unaligned_le32(entry->data);
+			*controller_baudrate = btrtl_convert_baudrate(
+							*device_baudrate);
+
+			if (entry->len >= 13)
+				*flow_control = !!(entry->data[12] & BIT(2));
+			else
+				*flow_control = false;
+
+			found = true;
+			break;
+
+		default:
+			bt_dev_dbg(hdev,
+				   "rtl: skipping config entry 0x%x (len %u)",
+				   le16_to_cpu(entry->offset), entry->len);
+			break;
+		};
+
+		i += sizeof(*entry) + entry->len;
+	}
+
+	if (!found) {
+		bt_dev_err(hdev, "rtl: no UART config entry found");
+		return -ENOENT;
+	}
+
+	bt_dev_dbg(hdev, "rtl: device baudrate = 0x%08x", *device_baudrate);
+	bt_dev_dbg(hdev, "rtl: controller baudrate = %u",
+		   *controller_baudrate);
+	bt_dev_dbg(hdev, "rtl: flow control %d", *flow_control);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(btrtl_get_uart_settings);
+
 MODULE_AUTHOR("Daniel Drake <[email protected]>");
 MODULE_DESCRIPTION("Bluetooth support for Realtek devices ver " VERSION);
 MODULE_VERSION(VERSION);
drivers/bluetooth/btrtl.h +25 -0
index 21c28dcbe5e0..2e7856fa5ac7 100644
@@ -40,6 +40,18 @@ struct rtl_epatch_header {
 	__le16 num_patches;
 } __packed;
 
+struct rtl_vendor_config_entry {
+	__le16 offset;
+	__u8 len;
+	__u8 data[0];
+} __packed;
+
+struct rtl_vendor_config {
+	__le32 signature;
+	__le16 total_len;
+	struct rtl_vendor_config_entry entry[0];
+} __packed;
+
 #if IS_ENABLED(CONFIG_BT_RTL)
 
 struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev);
@@ -47,6 +59,10 @@ void btrtl_free(struct btrtl_device_info *btrtl_dev);
 int btrtl_download_firmware(struct hci_dev *hdev,
 			    struct btrtl_device_info *btrtl_dev);
 int btrtl_setup_realtek(struct hci_dev *hdev);
+int btrtl_get_uart_settings(struct hci_dev *hdev,
+			    struct btrtl_device_info *btrtl_dev,
+			    unsigned int *controller_baudrate,
+			    u32 *device_baudrate, bool *flow_control);
 
 #else
 
@@ -70,4 +86,13 @@ static inline int btrtl_setup_realtek(struct hci_dev *hdev)
 	return -EOPNOTSUPP;
 }
 
+static inline int btrtl_get_uart_settings(struct hci_dev *hdev,
+					  struct btrtl_device_info *btrtl_dev,
+					  unsigned int *controller_baudrate,
+					  u32 *device_baudrate,
+					  bool *flow_control)
+{
+	return -ENOENT;
+}
+
 #endif
Signature · 1 line
2.15.1
author reviewer
#14 lore raw

RFC v2 6/9 Bluetooth: btrtl: add support for the RTL8723BS and RTL8723DS chips

In reply to Martin Blumenstingl

The Realtek RTL8723BS and RTL8723DS chipsets are SDIO wifi chips. They also contain a Bluetooth module which is connected via UART to the host.

Realtek's userspace initialization tool (rtk_hciattach) differentiates these two via the HCI version and revision returned by the HCI_OP_READ_LOCAL_VERSION command.
Additionally we apply these checks only the for UART devices. Everything else is assumed to be a "RTL8723B" which was originally supported by the driver (communicating via USB).

---
 drivers/bluetooth/btrtl.c | 32 ++++++++++++++++++++++++++++++--
 1 file changed, 30 insertions(+), 2 deletions(-)
drivers/bluetooth/btrtl.c +30 -2
index b6e8da4b73f8..25e9743643f5 100644
@@ -418,9 +418,33 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev)
 		has_rom_version = false;
 		break;
 	case RTL_ROM_LMP_8723B:
-		fw_name = "rtl_bt/rtl8723b_fw.bin";
-		cfg_name = "rtl_bt/rtl8723b_config.bin";
+		/* all variants support reading the ROM version */
 		has_rom_version = true;
+
+		/*
+		 * RTL8723 devices exist in different variants:
+		 * - RTL8723BS (SDIO chip with UART Bluetooth)
+		 * - RTL8723DS (SDIO chip with UART Bluetooth)
+		 * - for backwards-compatibility everything else is assumed to
+		 *   be an RTL8723B communicating over USB
+		 *
+		 * Only UART devices really need the config because that
+		 * contains the UART speed / flow control settings.
+		 */
+		if (hdev->bus == HCI_UART && resp->hci_ver == 6 &&
+		    le16_to_cpu(resp->hci_rev) == 0xb) {
+			fw_name = "rtl_bt/rtl8723bs_fw.bin";
+			cfg_name = "rtl_bt/rtl8723bs_config.bin";
+			cfg_needed = true;
+		} else if (hdev->bus == HCI_UART && resp->hci_ver == 8 &&
+			   le16_to_cpu(resp->hci_rev) == 0xd) {
+			fw_name = "rtl_bt/rtl8723ds_fw.bin";
+			cfg_name = "rtl_bt/rtl872ds_config.bin";
+			cfg_needed = true;
+		} else {
+			fw_name = "rtl_bt/rtl8723b_fw.bin";
+			cfg_name = "rtl_bt/rtl8723b_config.bin";
+		}
 		break;
 	case RTL_ROM_LMP_8821A:
 		fw_name = "rtl_bt/rtl8821a_fw.bin";
@@ -637,6 +661,10 @@ MODULE_LICENSE("GPL");
 MODULE_FIRMWARE("rtl_bt/rtl8723a_fw.bin");
 MODULE_FIRMWARE("rtl_bt/rtl8723b_fw.bin");
 MODULE_FIRMWARE("rtl_bt/rtl8723b_config.bin");
+MODULE_FIRMWARE("rtl_bt/rtl8723bs_fw.bin");
+MODULE_FIRMWARE("rtl_bt/rtl8723bs_config.bin");
+MODULE_FIRMWARE("rtl_bt/rtl8723ds_fw.bin");
+MODULE_FIRMWARE("rtl_bt/rtl8723ds_config.bin");
 MODULE_FIRMWARE("rtl_bt/rtl8761a_fw.bin");
 MODULE_FIRMWARE("rtl_bt/rtl8761a_config.bin");
 MODULE_FIRMWARE("rtl_bt/rtl8821a_fw.bin");
Signature · 1 line
2.15.1
author reviewer
#15 lore raw

RFC v2 7/9 bluetooth: btrtl: load the config blob from devicetree when available

In reply to Martin Blumenstingl

Some Realtek bluetooth devices need a "config" blob. The btrtl driver currently only allows loading this config blob via the request_firmware mechanism.

The UART Bluetooth chips use this config blob to specify the baudrate, whether flow control is used and some other unknown bits. This means that the config blob is board-specific - thus loading it via request_firmware means that the rootfs is tied to a specific board.

The UART Bluetooth chips are implemented through serdev. This means there is also a devicetree node which describes the Bluetooth chip. Thus we can also load the blob from the devicetree node to keep the filesystem independent of any board configuration data. In the future this could be extended to support ACPI as well (in case that's needed).

Parse the devicetree node if it exists and obtain the config blob from there. Otherwise fall back to using the "old" request_firmware mechanism.

---
 drivers/bluetooth/btrtl.c | 43 +++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 41 insertions(+), 2 deletions(-)
drivers/bluetooth/btrtl.c +41 -2
index 25e9743643f5..1494269cf4d7 100644
@@ -18,6 +18,7 @@
 #include <linux/module.h>
 #include <linux/firmware.h>
 #include <asm/unaligned.h>
+#include <linux/of.h>
 #include <linux/usb.h>
 
 #include <net/bluetooth/bluetooth.h>
@@ -379,6 +380,41 @@ void btrtl_free(struct btrtl_device_info *btrtl_dev)
 }
 EXPORT_SYMBOL_GPL(btrtl_free);
 
+static int rtl_load_config_from_dt(struct hci_dev *hdev,
+				   struct btrtl_device_info *btrtl_dev)
+{
+	struct device_node *np = hdev->dev.parent->of_node;
+	int ret, config_len;
+
+	if (!of_device_is_available(np))
+		return -ENOENT;
+
+	if (!of_find_property(np, "realtek,config-data", NULL))
+		return -ENOENT;
+
+	config_len = of_property_count_u8_elems(np, "realtek,config-data");
+	if (config_len <= 0)
+		return -ENOENT;
+
+	btrtl_dev->cfg_data = kzalloc(config_len, GFP_KERNEL);
+	if (!btrtl_dev->cfg_data)
+		return -ENOMEM;
+
+	ret = of_property_read_u8_array(np, "realtek,config-data",
+					btrtl_dev->cfg_data, config_len);
+	if (ret) {
+		kfree(btrtl_dev->cfg_data);
+		return ret;
+	}
+
+	btrtl_dev->cfg_len = config_len;
+
+	bt_dev_dbg(hdev, "rtl: using config data with len %d from DT",
+		   config_len);
+
+	return 0;
+}
+
 struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev)
 {
 	struct btrtl_device_info *btrtl_dev;
@@ -480,12 +516,15 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev)
 		goto err_free;
 	}
 
-	if (cfg_name) {
+	/* try loading the config blob from device-tree first: */
+	ret = rtl_load_config_from_dt(hdev, btrtl_dev);
+	/* fall back to loading the config via request_firmware: */
+	if (ret && cfg_name) {
 		btrtl_dev->cfg_len = rtl_load_file(hdev, cfg_name,
 						   &btrtl_dev->cfg_data);
 		if (cfg_needed && btrtl_dev->cfg_len <= 0) {
 			bt_dev_err(hdev,
-				   "mandatory config file %s not found\n",
+				   "mandatory config blob not found in %s or DT\n",
 				   cfg_name);
 			ret = btrtl_dev->fw_len;
 			goto err_free;
Signature · 1 line
2.15.1
maintainer
#16 lore raw

RFC v2 7/9 bluetooth: btrtl: load the config blob from devicetree when available

Hi Martin,

Quoted 15 lines

Some Realtek bluetooth devices need a "config" blob. The btrtl driver currently only allows loading this config blob via the request_firmware mechanism.

The UART Bluetooth chips use this config blob to specify the baudrate, whether flow control is used and some other unknown bits. This means that the config blob is board-specific - thus loading it via request_firmware means that the rootfs is tied to a specific board.

The UART Bluetooth chips are implemented through serdev. This means there is also a devicetree node which describes the Bluetooth chip. Thus we can also load the blob from the devicetree node to keep the filesystem independent of any board configuration data. In the future this could be extended to support ACPI as well (in case that's needed).

Quoted 3 lines

Parse the devicetree node if it exists and obtain the config blob from there. Otherwise fall back to using the "old" request_firmware mechanism.

where are these config blobs coming from? I think we also need to give people a helping hand on how to add them to DT. I still wonder if the only pieces we are using are the UART config, then maybe skipping the config blob and allowing for clear named values in DT might be better.

I might have asked before, but can we get a userspace similar to nokfw included in bluez.git that can parse and maybe even create/modify these blobs.

Regards

Marcel

#17 lore raw

RFC v2 7/9 bluetooth: btrtl: load the config blob from devicetree when available

Quoted 17 lines · Marcel Holtmann

Hi Martin,

Quoted 15 lines

Some Realtek bluetooth devices need a "config" blob. The btrtl driver currently only allows loading this config blob via the request_firmware mechanism.

The UART Bluetooth chips use this config blob to specify the baudrate, whether flow control is used and some other unknown bits. This means that the config blob is board-specific - thus loading it via request_firmware means that the rootfs is tied to a specific board.

The UART Bluetooth chips are implemented through serdev. This means there is also a devicetree node which describes the Bluetooth chip. Thus we can also load the blob from the devicetree node to keep the filesystem independent of any board configuration data. In the future this could be extended to support ACPI as well (in case that's needed).

Quoted 5 lines
Quoted 3 lines

Parse the devicetree node if it exists and obtain the config blob from there. Otherwise fall back to using the "old" request_firmware mechanism.

where are these config blobs coming from? I think we also need to give people a helping hand on how to add them to DT. I still wonder if the only pieces we are using are the UART config, then maybe skipping the config blob and allowing for clear named values in DT might be better.

What about x86 platforms where we do not have DT (I didn't check but I don't think that the UART config in that case is shipped in the ACPI tables)?

Cheers,

Signature · 1 line
Carlo Caione  |  +44.7384.69.16.04  |  Endless
maintainer
#18 lore raw

RFC v2 7/9 bluetooth: btrtl: load the config blob from devicetree when available

Hi Carlo,

Quoted 19 lines
Quoted 19 lines
Quoted 18 lines

Some Realtek bluetooth devices need a "config" blob. The btrtl driver currently only allows loading this config blob via the request_firmware mechanism.

The UART Bluetooth chips use this config blob to specify the baudrate, whether flow control is used and some other unknown bits. This means that the config blob is board-specific - thus loading it via request_firmware means that the rootfs is tied to a specific board.

The UART Bluetooth chips are implemented through serdev. This means there is also a devicetree node which describes the Bluetooth chip. Thus we can also load the blob from the devicetree node to keep the filesystem independent of any board configuration data. In the future this could be extended to support ACPI as well (in case that's needed).

Parse the devicetree node if it exists and obtain the config blob from there. Otherwise fall back to using the "old" request_firmware mechanism.

Quoted 5 lines
Quoted 1 line

where are these config blobs coming from? I think we also need to give people a helping hand on how to add them to DT. I still wonder if the only pieces we are using are the UART config, then maybe skipping the config blob and allowing for clear named values in DT might be better.

What about x86 platforms where we do not have DT (I didn't check but I don't think that the UART config in that case is shipped in the ACPI tables)?

if we have this hardware in x86 systems, then I would really like to see ACPI table dumps. Some pieces might need hardcoding based on ACPI ID.

Regards

Marcel

#19 lore raw

RFC v2 7/9 bluetooth: btrtl: load the config blob from devicetree when available

Quoted 21 lines · Marcel Holtmann

Hi Carlo,

Quoted 19 lines
Quoted 19 lines
Quoted 18 lines

Some Realtek bluetooth devices need a "config" blob. The btrtl driver currently only allows loading this config blob via the request_firmware mechanism.

The UART Bluetooth chips use this config blob to specify the baudrate, whether flow control is used and some other unknown bits. This means that the config blob is board-specific - thus loading it via request_firmware means that the rootfs is tied to a specific board.

The UART Bluetooth chips are implemented through serdev. This means there is also a devicetree node which describes the Bluetooth chip. Thus we can also load the blob from the devicetree node to keep the filesystem independent of any board configuration data. In the future this could be extended to support ACPI as well (in case that's needed).

Parse the devicetree node if it exists and obtain the config blob from there. Otherwise fall back to using the "old" request_firmware mechanism.

Quoted 7 lines
Quoted 5 lines
Quoted 1 line

where are these config blobs coming from? I think we also need to give people a helping hand on how to add them to DT. I still wonder if the only pieces we are using are the UART config, then maybe skipping the config blob and allowing for clear named values in DT might be better.

What about x86 platforms where we do not have DT (I didn't check but I don't think that the UART config in that case is shipped in the ACPI tables)?

if we have this hardware in x86 systems, then I would really like to see ACPI table dumps. Some pieces might need hardcoding based on ACPI ID.

Yes, we have, especially on cherry-trail SoCs. In [0] the DSDT of a cherry-trail laptop shipping the rtl8723bs (device OBDA8723).

[0] https://gist.github.com/carlocaione/82bff95ababb67dd33f52a86e94ce3ff

Cheers,

Signature · 1 line
Carlo Caione  |  +44.7384.69.16.04  |  Endless
maintainer
#20 lore raw

RFC v2 7/9 bluetooth: btrtl: load the config blob from devicetree when available

Hi Carlo,

Quoted 25 lines
Quoted 25 lines
Quoted 24 lines
Quoted 15 lines
Quoted 15 lines

Some Realtek bluetooth devices need a "config" blob. The btrtl driver currently only allows loading this config blob via the request_firmware mechanism.

The UART Bluetooth chips use this config blob to specify the baudrate, whether flow control is used and some other unknown bits. This means that the config blob is board-specific - thus loading it via request_firmware means that the rootfs is tied to a specific board.

The UART Bluetooth chips are implemented through serdev. This means there is also a devicetree node which describes the Bluetooth chip. Thus we can also load the blob from the devicetree node to keep the filesystem independent of any board configuration data. In the future this could be extended to support ACPI as well (in case that's needed).

Quoted 5 lines
Quoted 3 lines

Parse the devicetree node if it exists and obtain the config blob from there. Otherwise fall back to using the "old" request_firmware mechanism.

where are these config blobs coming from? I think we also need to give people a helping hand on how to add them to DT. I still wonder if the only pieces we are using are the UART config, then maybe skipping the config blob and allowing for clear named values in DT might be better.

What about x86 platforms where we do not have DT (I didn't check but I don't think that the UART config in that case is shipped in the ACPI tables)?

Quoted 6 lines
Quoted 1 line

if we have this hardware in x86 systems, then I would really like to see ACPI table dumps. Some pieces might need hardcoding based on ACPI ID.

Yes, we have, especially on cherry-trail SoCs. In [0] the DSDT of a cherry-trail laptop shipping the rtl8723bs (device OBDA8723).

[0] https://gist.github.com/carlocaione/82bff95ababb67dd33f52a86e94ce3ff

so the BTHx entries normally come at least with the UART configuration. It would be useful check if it is actually correct. And then I think similar handling like what is done in hci_bcm.c and hci_intel.c needs to happen here.

I think that we extended serdev to ACPI as well. Don?t recall of the top of my head if these patches were merged or not. But if they are then it is as simple as a serdev DT based driver. Just add the appropriate _HID and got from there.

However now I think that moving towards making hci_h5.c more like generic abstraction like hci_h4.c and having a hci_rtl.c be specific for Realtek chips seems a bit cleaner direction. Frankly only H:4 and H:5 plain protocols should be used by btattach. And all others should go via serdev.

Regards

Marcel

author reviewer
#21 lore raw

RFC v2 7/9 bluetooth: btrtl: load the config blob from devicetree when available

Hi Marcel, Hi Carlo,

Quoted 27 lines · Marcel Holtmann

Hi Carlo,

Quoted 25 lines
Quoted 25 lines
Quoted 24 lines
Quoted 15 lines
Quoted 15 lines

Some Realtek bluetooth devices need a "config" blob. The btrtl driver currently only allows loading this config blob via the request_firmware mechanism.

The UART Bluetooth chips use this config blob to specify the baudrate, whether flow control is used and some other unknown bits. This means that the config blob is board-specific - thus loading it via request_firmware means that the rootfs is tied to a specific board.

The UART Bluetooth chips are implemented through serdev. This means there is also a devicetree node which describes the Bluetooth chip. Thus we can also load the blob from the devicetree node to keep the filesystem independent of any board configuration data. In the future this could be extended to support ACPI as well (in case that's needed).

Quoted 5 lines
Quoted 3 lines

Parse the devicetree node if it exists and obtain the config blob from there. Otherwise fall back to using the "old" request_firmware mechanism.

where are these config blobs coming from? I think we also need to give people a helping hand on how to add them to DT. I still wonder if the only pieces we are using are the UART config, then maybe skipping the config blob and allowing for clear named values in DT might be better.

What about x86 platforms where we do not have DT (I didn't check but I don't think that the UART config in that case is shipped in the ACPI tables)?

Quoted 8 lines
Quoted 6 lines
Quoted 1 line

if we have this hardware in x86 systems, then I would really like to see ACPI table dumps. Some pieces might need hardcoding based on ACPI ID.

Yes, we have, especially on cherry-trail SoCs. In [0] the DSDT of a cherry-trail laptop shipping the rtl8723bs (device OBDA8723).

[0] https://gist.github.com/carlocaione/82bff95ababb67dd33f52a86e94ce3ff

so the BTHx entries normally come at least with the UART configuration. It would be useful check if it is actually correct. And then I think similar handling like what is done in hci_bcm.c and hci_intel.c needs to happen here.

what I can see is (which also matches the settings I use on my Amlogic based boards):
- initial speed = 115200
- 8 data bits
- 1 stop bit
- even parity
- HW flow control enabled

Quoted 1 line

I think that we extended serdev to ACPI as well. Don?t recall of the top of my head if these patches were merged or not. But if they are then it is as simple as a serdev DT based driver. Just add the appropriate _HID and got from there.

yes, serdev recently gained ACPI support iirc
however, can we agree on implementing this step-by-step:
- I do not have any hardware that uses a RTL8723BS or RTL8723DS and ACPI (I have these chips in Amlogic SoC based "TV boxes" where DT is the leading firmware, so I cannot test the ACPI part)
- my goal is to get rid of the rtk_hciattach userspace tool dependency (mostly Allwinner and Amlogic based devices will benefit from this)
- it would be great if I could get feedback what to consider or to avoid when implementing this so ACPI support can be added easily on top of my patches

Quoted 1 line

However now I think that moving towards making hci_h5.c more like generic abstraction like hci_h4.c and having a hci_rtl.c be specific for Realtek chips seems a bit cleaner direction. Frankly only H:4 and H:5 plain protocols should be used by btattach. And all others should go via serdev.

I agree with you here (as already discussed in the other patch)

Regards
Martin

author reviewer
#22 lore raw

RFC v2 7/9 bluetooth: btrtl: load the config blob from devicetree when available

In reply to Carlo Caione

Hi Carlo,

Quoted 28 lines · Carlo Caione
Quoted 27 lines · Marcel Holtmann

Hi Carlo,

Quoted 24 lines
Quoted 15 lines
Quoted 15 lines

Some Realtek bluetooth devices need a "config" blob. The btrtl driver currently only allows loading this config blob via the request_firmware mechanism.

The UART Bluetooth chips use this config blob to specify the baudrate, whether flow control is used and some other unknown bits. This means that the config blob is board-specific - thus loading it via request_firmware means that the rootfs is tied to a specific board.

The UART Bluetooth chips are implemented through serdev. This means there is also a devicetree node which describes the Bluetooth chip. Thus we can also load the blob from the devicetree node to keep the filesystem independent of any board configuration data. In the future this could be extended to support ACPI as well (in case that's needed).

Quoted 5 lines
Quoted 3 lines

Parse the devicetree node if it exists and obtain the config blob from there. Otherwise fall back to using the "old" request_firmware mechanism.

where are these config blobs coming from? I think we also need to give people a helping hand on how to add them to DT. I still wonder if the only pieces we are using are the UART config, then maybe skipping the config blob and allowing for clear named values in DT might be better.

What about x86 platforms where we do not have DT (I didn't check but I don't think that the UART config in that case is shipped in the ACPI tables)?

Quoted 6 lines
Quoted 1 line

if we have this hardware in x86 systems, then I would really like to see ACPI table dumps. Some pieces might need hardcoding based on ACPI ID.

Yes, we have, especially on cherry-trail SoCs. In [0] the DSDT of a cherry-trail laptop shipping the rtl8723bs (device OBDA8723).

[0] https://gist.github.com/carlocaione/82bff95ababb67dd33f52a86e94ce3ff

so this shows that the UART settings (initial baudrate, HW flow control, etc.) are part of the DSDT
however, the actual config blob is not

the description of this patch explains: "Parse the devicetree node ... [or] ... fall back to using the "old" request_firmware mechanism." do you have any other solution in mind?

Regards
Martin

#23 lore raw

RFC v2 7/9 bluetooth: btrtl: load the config blob from devicetree when available

Quoted 1 line · Martin Blumenstingl

Hi Carlo,

Hi Martin,

Quoted 34 lines · Carlo Caione
Quoted 33 lines · Marcel Holtmann
Quoted 21 lines

Hi Carlo,

Quoted 19 lines
Quoted 19 lines
Quoted 18 lines

Some Realtek bluetooth devices need a "config" blob. The btrtl driver currently only allows loading this config blob via the request_firmware mechanism.

The UART Bluetooth chips use this config blob to specify the baudrate, whether flow control is used and some other unknown bits. This means that the config blob is board-specific - thus loading it via request_firmware means that the rootfs is tied to a specific board.

The UART Bluetooth chips are implemented through serdev. This means there is also a devicetree node which describes the Bluetooth chip. Thus we can also load the blob from the devicetree node to keep the filesystem independent of any board configuration data. In the future this could be extended to support ACPI as well (in case that's needed).

Parse the devicetree node if it exists and obtain the config blob from there. Otherwise fall back to using the "old" request_firmware mechanism.

Quoted 7 lines
Quoted 5 lines
Quoted 1 line

where are these config blobs coming from? I think we also need to give people a helping hand on how to add them to DT. I still wonder if the only pieces we are using are the UART config, then maybe skipping the config blob and allowing for clear named values in DT might be better.

What about x86 platforms where we do not have DT (I didn't check but I don't think that the UART config in that case is shipped in the ACPI tables)?

if we have this hardware in x86 systems, then I would really like to see ACPI table dumps. Some pieces might need hardcoding based on ACPI ID.

Yes, we have, especially on cherry-trail SoCs. In [0] the DSDT of a cherry-trail laptop shipping the rtl8723bs (device OBDA8723).

Quoted 8 lines
Quoted 1 line

[0] https://gist.github.com/carlocaione/82bff95ababb67dd33f52a86e94ce3ff

so this shows that the UART settings (initial baudrate, HW flow control, etc.) are part of the DSDT
however, the actual config blob is not

the description of this patch explains: "Parse the devicetree node ... [or] ... fall back to using the "old" request_firmware mechanism." do you have any other solution in mind?

As Marcel suggested we can assume that the information in the DSDT is correct so that we can get rid of the config blob also for x86 platforms (assuming that the only useful information in the config blobs is the UART configuration).

Adding the ACPI support on top of your patches is (hopefully) trivial, just follow what was done for hci_bcm.c, basically adding a new _HID and reading the configuration for GPIOs and UART, all the rest should be transparent for serdev.

I'll test your patches on the hardware I have.

Cheers,

Signature · 1 line
Carlo Caione  |  +44.7384.69.16.04  |  Endless
author reviewer
#24 lore raw

RFC v2 7/9 bluetooth: btrtl: load the config blob from devicetree when available

Hi Carlo,

Quoted 45 lines · Carlo Caione
Quoted 1 line · Martin Blumenstingl

Hi Carlo,

Hi Martin,

Quoted 39 lines · Carlo Caione
Quoted 28 lines · Marcel Holtmann
Quoted 27 lines

Hi Carlo,

Quoted 24 lines
Quoted 15 lines
Quoted 15 lines

Some Realtek bluetooth devices need a "config" blob. The btrtl driver currently only allows loading this config blob via the request_firmware mechanism.

The UART Bluetooth chips use this config blob to specify the baudrate, whether flow control is used and some other unknown bits. This means that the config blob is board-specific - thus loading it via request_firmware means that the rootfs is tied to a specific board.

The UART Bluetooth chips are implemented through serdev. This means there is also a devicetree node which describes the Bluetooth chip. Thus we can also load the blob from the devicetree node to keep the filesystem independent of any board configuration data. In the future this could be extended to support ACPI as well (in case that's needed).

Quoted 5 lines
Quoted 3 lines

Parse the devicetree node if it exists and obtain the config blob from there. Otherwise fall back to using the "old" request_firmware mechanism.

where are these config blobs coming from? I think we also need to give people a helping hand on how to add them to DT. I still wonder if the only pieces we are using are the UART config, then maybe skipping the config blob and allowing for clear named values in DT might be better.

What about x86 platforms where we do not have DT (I didn't check but I don't think that the UART config in that case is shipped in the ACPI tables)?

Quoted 6 lines
Quoted 1 line

if we have this hardware in x86 systems, then I would really like to see ACPI table dumps. Some pieces might need hardcoding based on ACPI ID.

Yes, we have, especially on cherry-trail SoCs. In [0] the DSDT of a cherry-trail laptop shipping the rtl8723bs (device OBDA8723).

[0] https://gist.github.com/carlocaione/82bff95ababb67dd33f52a86e94ce3ff

so this shows that the UART settings (initial baudrate, HW flow control, etc.) are part of the DSDT
however, the actual config blob is not

Quoted 8 lines
Quoted 3 lines

the description of this patch explains: "Parse the devicetree node ... [or] ... fall back to using the "old" request_firmware mechanism." do you have any other solution in mind?

As Marcel suggested we can assume that the information in the DSDT is correct so that we can get rid of the config blob also for x86 platforms (assuming that the only useful information in the config blobs is the UART configuration).

in my tests I tried to send only the firmware without the config to my RTL8723BS. unfortunately the last firmware chunk (sent to the controller) times out in that case (even if I set a proper baudrate before or if I specify no baudrate at all and keep the serdev at 115200)
have you tested this (= uploading the firmware without the config blob) on your x86 board before?

so far the following solutions for the config blob were discussed:
1) put the config blob in userspace (/lib/firmware/...) -> not good because it makes the rootfs board-specific
2) auto-generate the config blob -> didn't work for me, last firmware chunk times out (just as if I had no config at all)
3) putting the config blob in DT -> possible but not very nice to read

I had another idea:
what if we mix solution 1) and 2)?
the idea: load the config blob from userspace (/lib/firmware/...) and update it's settings with the values we got from devicetree-properties / DSDT
I have not tested this yet, but I just want to hear everyone's (at least Marcel, Rob and Carlo) opinion on that
(this would also allow us to fully auto-generate the config blob in the future once we figure out how to do that)

Quoted 4 lines

Adding the ACPI support on top of your patches is (hopefully) trivial, just follow what was done for hci_bcm.c, basically adding a new _HID and reading the configuration for GPIOs and UART, all the rest should be transparent for serdev.

thanks for the reference to hci_bcm.c - I will have a look at this for the next version
one question: "_HID" would be OBDA8723 in our case?

Quoted 1 line

I'll test your patches on the hardware I have.

great, thank you!

Regards
Martin

#25 lore raw

RFC v2 7/9 bluetooth: btrtl: load the config blob from devicetree when available

Quoted 1 line · Martin Blumenstingl

Hi Carlo,

Hi Martin,

Quoted 9 lines
Quoted 4 lines

As Marcel suggested we can assume that the information in the DSDT is correct so that we can get rid of the config blob also for x86 platforms (assuming that the only useful information in the config blobs is the UART configuration).

in my tests I tried to send only the firmware without the config to my RTL8723BS. unfortunately the last firmware chunk (sent to the controller) times out in that case (even if I set a proper baudrate before or if I specify no baudrate at all and keep the serdev at 115200)

What's in the config blobs besides UART configuration?

It's odd because reading into hciattach_rtk.c it seems that the config file is actually only used by the userspace tools (hciattach) to retrieve the UART configuration and nothing else, whereas in the kernel driver the config blob is appended to the firmware.

Quoted 2 lines

have you tested this (= uploading the firmware without the config blob) on your x86 board before?

No, on the cherry-trail I have I'm using hciattach to upload the firmware and AFAICT only the firmware is uploaded and (as written before) the config blob is only used to get the UART configuration.

Quoted 7 lines

so far the following solutions for the config blob were discussed:
1) put the config blob in userspace (/lib/firmware/...) -> not good because it makes the rootfs board-specific
2) auto-generate the config blob -> didn't work for me, last firmware chunk times out (just as if I had no config at all)
3) putting the config blob in DT -> possible but not very nice to read

Quoted 5 lines

I had another idea:
what if we mix solution 1) and 2)?
the idea: load the config blob from userspace (/lib/firmware/...) and update it's settings with the values we got from devicetree-properties / DSDT

If you are shipping already the config blob in userspace I don't see why you would do the update since you have already all the info you need.
I would rather check why (2) didn't work fine. Have you any documentation how the config blobs are generated? The code in hciattach_rtk.c seems a good starting point.

Quoted 5 lines

I have not tested this yet, but I just want to hear everyone's (at least Marcel, Rob and Carlo) opinion on that
(this would also allow us to fully auto-generate the config blob in the future once we figure out how to do that)

Quoted 7 lines
Quoted 4 lines

Adding the ACPI support on top of your patches is (hopefully) trivial, just follow what was done for hci_bcm.c, basically adding a new _HID and reading the configuration for GPIOs and UART, all the rest should be transparent for serdev.

thanks for the reference to hci_bcm.c - I will have a look at this for the next version
one question: "_HID" would be OBDA8723 in our case?

Yes

Quoted 2 lines
Quoted 1 line

I'll test your patches on the hardware I have.

great, thank you!

Cheers!

Signature · 1 line
Carlo Caione  |  +44.7384.69.16.04  |  Endless
maintainer
#26 lore raw

RFC v2 7/9 bluetooth: btrtl: load the config blob from devicetree when available

Hi Carlo,

Quoted 11 lines
Quoted 9 lines
Quoted 4 lines

As Marcel suggested we can assume that the information in the DSDT is correct so that we can get rid of the config blob also for x86 platforms (assuming that the only useful information in the config blobs is the UART configuration).

in my tests I tried to send only the firmware without the config to my RTL8723BS. unfortunately the last firmware chunk (sent to the controller) times out in that case (even if I set a proper baudrate before or if I specify no baudrate at all and keep the serdev at 115200)

What's in the config blobs besides UART configuration?

is anybody writing a rtlfw.c tool (like nokfw.c) so that we can print out what we actually have in these config files?

Quoted 4 lines

It's odd because reading into hciattach_rtk.c it seems that the config file is actually only used by the userspace tools (hciattach) to retrieve the UART configuration and nothing else, whereas in the kernel driver the config blob is appended to the firmware.

Frankly, I am inclined to not use the config file even for DT based system and just allow specifying the UART settings via normal DT properties like we do for Broadcom and others.

Quoted 20 lines
Quoted 2 lines

have you tested this (= uploading the firmware without the config blob) on your x86 board before?

No, on the cherry-trail I have I'm using hciattach to upload the firmware and AFAICT only the firmware is uploaded and (as written before) the config blob is only used to get the UART configuration.

Quoted 7 lines

so far the following solutions for the config blob were discussed:
1) put the config blob in userspace (/lib/firmware/...) -> not good because it makes the rootfs board-specific
2) auto-generate the config blob -> didn't work for me, last firmware chunk times out (just as if I had no config at all)
3) putting the config blob in DT -> possible but not very nice to read

Quoted 5 lines

I had another idea:
what if we mix solution 1) and 2)?
the idea: load the config blob from userspace (/lib/firmware/...) and update it's settings with the values we got from devicetree-properties / DSDT

Quoted 6 lines

If you are shipping already the config blob in userspace I don't see why you would do the update since you have already all the info you need.
I would rather check why (2) didn't work fine. Have you any documentation how the config blobs are generated? The code in hciattach_rtk.c seems a good starting point.

I would almost bet it needs a HCI commands or some HCI command is embedded in the config blob somewhere that essentially tells the firmware it is done and ready. Hence my ask above, we need to know what is in these config file.

Even settings like crystal timing or some RF pieces or even enabled/disabled features are board specific and should be in DT rather than a config blob on the general purpose filesystem.

Regards

Marcel

maintainer
#27 lore raw

RFC v2 7/9 bluetooth: btrtl: load the config blob from devicetree when available

Hi Carlo,

Quoted 12 lines
Quoted 11 lines
Quoted 9 lines
Quoted 4 lines

As Marcel suggested we can assume that the information in the DSDT is correct so that we can get rid of the config blob also for x86 platforms (assuming that the only useful information in the config blobs is the UART configuration).

in my tests I tried to send only the firmware without the config to my RTL8723BS. unfortunately the last firmware chunk (sent to the controller) times out in that case (even if I set a proper baudrate before or if I specify no baudrate at all and keep the serdev at 115200)

What's in the config blobs besides UART configuration?

Quoted 8 lines

is anybody writing a rtlfw.c tool (like nokfw.c) so that we can print out what we actually have in these config files?

Quoted 4 lines

It's odd because reading into hciattach_rtk.c it seems that the config file is actually only used by the userspace tools (hciattach) to retrieve the UART configuration and nothing else, whereas in the kernel driver the config blob is appended to the firmware.

Frankly, I am inclined to not use the config file even for DT based system and just allow specifying the UART settings via normal DT properties like we do for Broadcom and others.

so I googled for a few config files and this is what this turns into:

Analyzing rtl8723d_config_1000000_noflow.dms

Signature:   0x8723ab55
Data length: 41
len=1   offset=f4,{ 01 }
len=2   offset=f6,{ 81 00 }
len=2   offset=fa,{ 12 80 }
len=16  offset=0c,{ 04 50 00 00 50 c5 ea 19 e1 1b fd af 5b 01 a4 0b }
len=1   offset=d9,{ 0f }
len=1   offset=e4,{ 08 }
Analyzing rtl8723d_config.dms
Signature:   0x8723ab55
Data length: 41
len=1   offset=f4,{ 01 }
len=2   offset=f6,{ 81 00 }
len=2   offset=fa,{ 12 80 }
len=16  offset=0c,{ 02 80 92 04 50 c5 ea 19 e1 1b fd af 5f 01 a4 0b }
len=1   offset=d9,{ 0f }
len=1   offset=e4,{ 08 }
Analyzing rtl8822b_config.bin
Signature:   0x8723ab55
Data length: 8

len=1 offset=d9,{ 0f }
len=1 offset=e4,{ 08 }

The first two are some UART based ones and the last one is USB based.

So the 0x3c offset seems to be the BD_ADDR and 0x0c offset is the UART configuration. It would be good to know which settings the other ones control.

Also the 16 octet UART config blob seems to be decoded like this:

	uart_config {
		le32 baudrate;
		u8[8] reserved1;
		u8 flowctl;
		u8[3] reserved2;
	}

Actually hciattach_rtk just takes the baud rate and and hardware flow control bit out of this file. That is clearly two things that are better written in plain text in the DT file.

Regards

Marcel

maintainer
#28 lore raw

RFC v2 7/9 bluetooth: btrtl: load the config blob from devicetree when available

Hi Carlo,

Quoted 53 lines
Quoted 12 lines
Quoted 11 lines
Quoted 9 lines
Quoted 4 lines

As Marcel suggested we can assume that the information in the DSDT is correct so that we can get rid of the config blob also for x86 platforms (assuming that the only useful information in the config blobs is the UART configuration).

in my tests I tried to send only the firmware without the config to my RTL8723BS. unfortunately the last firmware chunk (sent to the controller) times out in that case (even if I set a proper baudrate before or if I specify no baudrate at all and keep the serdev at 115200)

What's in the config blobs besides UART configuration?

Quoted 8 lines

is anybody writing a rtlfw.c tool (like nokfw.c) so that we can print out what we actually have in these config files?

Quoted 4 lines

It's odd because reading into hciattach_rtk.c it seems that the config file is actually only used by the userspace tools (hciattach) to retrieve the UART configuration and nothing else, whereas in the kernel driver the config blob is appended to the firmware.

Frankly, I am inclined to not use the config file even for DT based system and just allow specifying the UART settings via normal DT properties like we do for Broadcom and others.

so I googled for a few config files and this is what this turns into:

Analyzing rtl8723d_config_1000000_noflow.dms

Signature:   0x8723ab55
Data length: 41
len=1   offset=f4,{ 01 }
len=2   offset=f6,{ 81 00 }
len=2   offset=fa,{ 12 80 }
len=16  offset=0c,{ 04 50 00 00 50 c5 ea 19 e1 1b fd af 5b 01 a4 0b }
len=1   offset=d9,{ 0f }
len=1   offset=e4,{ 08 }
Analyzing rtl8723d_config.dms
Signature:   0x8723ab55
Data length: 41
len=1   offset=f4,{ 01 }
len=2   offset=f6,{ 81 00 }
len=2   offset=fa,{ 12 80 }
len=16  offset=0c,{ 02 80 92 04 50 c5 ea 19 e1 1b fd af 5f 01 a4 0b }
len=1   offset=d9,{ 0f }
len=1   offset=e4,{ 08 }
Analyzing rtl8822b_config.bin
Signature:   0x8723ab55
Data length: 8

len=1 offset=d9,{ 0f }
len=1 offset=e4,{ 08 }

The first two are some UART based ones and the last one is USB based.

So the 0x3c offset seems to be the BD_ADDR and 0x0c offset is the UART configuration. It would be good to know which settings the other ones control.

Also the 16 octet UART config blob seems to be decoded like this:

Quoted 8 lines
	uart_config {
		le32 baudrate;
		u8[8] reserved1;
		u8 flowctl;
		u8[3] reserved2;
	}

Actually hciattach_rtk just takes the baud rate and and hardware flow control bit out of this file. That is clearly two things that are better written in plain text in the DT file.

so this is actually some funny stuff if you start to understand it.

Analyzing rtl8723b_config.dms

Signature: 0x8723ab55
Data len:  38
len=8   offset=00f4,{ 01 00 00 00 05 50 00 00 }
len=16  offset=000c,{ 02 80 92 04 50 c5 ea 19 e1 1b f1 af 5f 01 a4 0b },UART_CONFIG
len=1   offset=0027,{ 63 }
len=1   offset=00fe,{ 01 }
Analyzing rtl8723d_config_1000000_noflow.dms
Signature: 0x8723ab55
Data len:  41
len=1   offset=00f4,{ 01 }
len=2   offset=00f6,{ 81 00 }
len=2   offset=00fa,{ 12 80 }
len=16  offset=000c,{ 04 50 00 00 50 c5 ea 19 e1 1b fd af 5b 01 a4 0b },UART_CONFIG
len=1   offset=00d9,{ 0f }
len=1   offset=00e4,{ 08 }

Seems like Realtek really defines memory offsets in this file and they can be defined in various different ways.

So 00f4,{ 01 00 00 00 05 50 00 00 } defines the whole PCM settings for interface 1 and 2. And 00f4,{ 01 } + 00f6,{ 81 00 } + 00fa,{ 12 80 } is the same PCM settings, but with only pieces of it defined.

This also means a 000c,{ 04 50 00 00 50 } for just setting the baud rate is as valid if flow control defaults to off and all other values are actual defaults. So code inside hciattach_rtk.c is also kind faulty on how it handles the flow control bit. It works if the config files all have offset 000c in it, but if not, then they are going funky.

Since these are efuse settings, I really wonder if there is just a HCI vendor command to read out the defaults and we use that to compare. And what I would really like to know is what these settings are suppose to change. Since even for USB, we are actually not even applying them.

Anyway, I am certain that for Realtek UART devices, we just want to specify max-speed DT property like we do with the others. And then maybe a flow-control DT property to control that one (following what nfcmrvl.txt does). We can use the rtlfw tool that I wrote to extract the values from Realtek provided config files. Frankly the PCM settings we have to deal with as well at some point. But that is also missing for Broadcom and others.

Also if there is no config file, we should be able to just assume no flow control, 115200 baud rate and H:5 as protocol. That means that almost all chips will just work. They are just slow since we do not deal with the max-speed setting.

At the end of the day, this is the same as for Broadcom and ACPI vs DT. This should be no different. The only bit nasty part is that this is H:5 as protocol and we have not really abstracted that one nicely to just have a tiny hci_rtl.c for vendor specific pieces.

Regards

Marcel

author reviewer
#29 lore raw

RFC v2 7/9 bluetooth: btrtl: load the config blob from devicetree when available

Hi Marcel,

thank you for digging into this!

Quoted 52 lines · Marcel Holtmann

Hi Carlo,

Quoted 50 lines
Quoted 12 lines
Quoted 11 lines
Quoted 9 lines
Quoted 4 lines

As Marcel suggested we can assume that the information in the DSDT is correct so that we can get rid of the config blob also for x86 platforms (assuming that the only useful information in the config blobs is the UART configuration).

in my tests I tried to send only the firmware without the config to my RTL8723BS. unfortunately the last firmware chunk (sent to the controller) times out in that case (even if I set a proper baudrate before or if I specify no baudrate at all and keep the serdev at 115200)

What's in the config blobs besides UART configuration?

Quoted 8 lines

is anybody writing a rtlfw.c tool (like nokfw.c) so that we can print out what we actually have in these config files?

Quoted 4 lines

It's odd because reading into hciattach_rtk.c it seems that the config file is actually only used by the userspace tools (hciattach) to retrieve the UART configuration and nothing else, whereas in the kernel driver the config blob is appended to the firmware.

Frankly, I am inclined to not use the config file even for DT based system and just allow specifying the UART settings via normal DT properties like we do for Broadcom and others.

so I googled for a few config files and this is what this turns into:

Analyzing rtl8723d_config_1000000_noflow.dms

Signature:   0x8723ab55
Data length: 41
len=1   offset=f4,{ 01 }
len=2   offset=f6,{ 81 00 }
len=2   offset=fa,{ 12 80 }
len=16  offset=0c,{ 04 50 00 00 50 c5 ea 19 e1 1b fd af 5b 01 a4 0b }
len=1   offset=d9,{ 0f }
len=1   offset=e4,{ 08 }
Analyzing rtl8723d_config.dms
Signature:   0x8723ab55
Data length: 41
len=1   offset=f4,{ 01 }
len=2   offset=f6,{ 81 00 }
len=2   offset=fa,{ 12 80 }
len=16  offset=0c,{ 02 80 92 04 50 c5 ea 19 e1 1b fd af 5f 01 a4 0b }
len=1   offset=d9,{ 0f }
len=1   offset=e4,{ 08 }
Analyzing rtl8822b_config.bin
Signature:   0x8723ab55
Data length: 8

len=1 offset=d9,{ 0f }
len=1 offset=e4,{ 08 }

The first two are some UART based ones and the last one is USB based.

So the 0x3c offset seems to be the BD_ADDR and 0x0c offset is the UART configuration. It would be good to know which settings the other ones control.

this matches my findings from the hciattach_rtk tool for rtl8723bs_bt and rtl8723ds_bt

Quoted 9 lines
Quoted 9 lines

Also the 16 octet UART config blob seems to be decoded like this:

      uart_config {
              le32 baudrate;
              u8[8] reserved1;
              u8 flowctl;
              u8[3] reserved2;
      }

I could not find this in any of the rtl8723bs_bt or rtl8723ds_bt sources so thank you for sharing this (even though this descriptin is missing a few bytes)!

Quoted 1 line
Quoted 1 line

Actually hciattach_rtk just takes the baud rate and and hardware flow control bit out of this file. That is clearly two things that are better written in plain text in the DT file.

this matches my findings apart from that hciattach_rtk also appends the config blob to the "firmware patch" that is being uploaded to the device
if you are brave you can have a look at [0]

Quoted 19 lines

so this is actually some funny stuff if you start to understand it.

Analyzing rtl8723b_config.dms

Signature: 0x8723ab55
Data len:  38
len=8   offset=00f4,{ 01 00 00 00 05 50 00 00 }
len=16  offset=000c,{ 02 80 92 04 50 c5 ea 19 e1 1b f1 af 5f 01 a4 0b },UART_CONFIG
len=1   offset=0027,{ 63 }
len=1   offset=00fe,{ 01 }
Analyzing rtl8723d_config_1000000_noflow.dms
Signature: 0x8723ab55
Data len:  41
len=1   offset=00f4,{ 01 }
len=2   offset=00f6,{ 81 00 }
len=2   offset=00fa,{ 12 80 }
len=16  offset=000c,{ 04 50 00 00 50 c5 ea 19 e1 1b fd af 5b 01 a4 0b },UART_CONFIG
len=1   offset=00d9,{ 0f }
len=1   offset=00e4,{ 08 }
Quoted 1 line

Seems like Realtek really defines memory offsets in this file and they can be defined in various different ways.

wow, that is interesting - I was wondering why they called it "offset" instead of "config_id" (or something similar)

Quoted 5 lines

So 00f4,{ 01 00 00 00 05 50 00 00 } defines the whole PCM settings for interface 1 and 2. And 00f4,{ 01 } + 00f6,{ 81 00 } + 00fa,{ 12 80 } is the same PCM settings, but with only pieces of it defined.

This also means a 000c,{ 04 50 00 00 50 } for just setting the baud rate is as valid if flow control defaults to off and all other values are actual defaults. So code inside hciattach_rtk.c is also kind faulty on how it handles the flow control bit. It works if the config files all have offset 000c in it, but if not, then they are going funky.

Since these are efuse settings, I really wonder if there is just a HCI vendor command to read out the defaults and we use that to compare. And what I would really like to know is what these settings are suppose to change. Since even for USB, we are actually not even applying them.

the idea with the vendor command to read out existing memory is interesting

based on the code in "btrtl.c" it seems that we are applying the settings from the config blob.
we are simply appending it at the end of the "firmware patch", see [1]

Quoted 1 line

Anyway, I am certain that for Realtek UART devices, we just want to specify max-speed DT property like we do with the others. And then maybe a flow-control DT property to control that one (following what nfcmrvl.txt does). We can use the rtlfw tool that I wrote to extract the values from Realtek provided config files. Frankly the PCM settings we have to deal with as well at some point. But that is also missing for Broadcom and others.

just to make sure I understand you correctly: would you generate the config blob in-memory (in a function in btrtl.c) and hardcode all unknown bits (the reserved bits in the UART config entry for example) or would you mandate that a config blob is present (so request_firmware can fetch it) for "high-speed" operation?

for PCM: I cannot test that anyways since the Amlogic platform does not have audio support yet

Quoted 1 line

Also if there is no config file, we should be able to just assume no flow control, 115200 baud rate and H:5 as protocol. That means that almost all chips will just work. They are just slow since we do not deal with the max-speed setting.

my problem so far was that uploading the firmware patch without appending a config blob (see [1]) broke the UART communication (it was not 115200 baud as before, and I also tried other speeds such as 1500000 and 1000000 baud - neither worked)
however, extracting the existing values from the efuse using a vendor command might give a hint why (maybe they are fallling back to some exotic baudrate in that case, etc...)

Quoted 1 line

At the end of the day, this is the same as for Broadcom and ACPI vs DT. This should be no different. The only bit nasty part is that this is H:5 as protocol and we have not really abstracted that one nicely to just have a tiny hci_rtl.c for vendor specific pieces.

yes, you already suggested that I should start making that re-usable (this is still on my TODO-list)

Regards
Martin

[0] https://github.com/NextThingCo/rtl8723ds_bt/blob/master/rtk_hciattach/hciattach_rtk.c#L2742
[1] https://elixir.free-electrons.com/linux/v4.14.11/source/drivers/bluetooth/btrtl.c#L377

maintainer
#30 lore raw

RFC v2 7/9 bluetooth: btrtl: load the config blob from devicetree when available

Hi Martin,

Quoted 66 lines
Quoted 47 lines
Quoted 47 lines
Quoted 12 lines
Quoted 11 lines
Quoted 9 lines
Quoted 4 lines

As Marcel suggested we can assume that the information in the DSDT is correct so that we can get rid of the config blob also for x86 platforms (assuming that the only useful information in the config blobs is the UART configuration).

in my tests I tried to send only the firmware without the config to my RTL8723BS. unfortunately the last firmware chunk (sent to the controller) times out in that case (even if I set a proper baudrate before or if I specify no baudrate at all and keep the serdev at 115200)

What's in the config blobs besides UART configuration?

Quoted 8 lines

is anybody writing a rtlfw.c tool (like nokfw.c) so that we can print out what we actually have in these config files?

Quoted 4 lines

It's odd because reading into hciattach_rtk.c it seems that the config file is actually only used by the userspace tools (hciattach) to retrieve the UART configuration and nothing else, whereas in the kernel driver the config blob is appended to the firmware.

Frankly, I am inclined to not use the config file even for DT based system and just allow specifying the UART settings via normal DT properties like we do for Broadcom and others.

so I googled for a few config files and this is what this turns into:

Analyzing rtl8723d_config_1000000_noflow.dms

Signature:   0x8723ab55
Data length: 41
len=1   offset=f4,{ 01 }
len=2   offset=f6,{ 81 00 }
len=2   offset=fa,{ 12 80 }
len=16  offset=0c,{ 04 50 00 00 50 c5 ea 19 e1 1b fd af 5b 01 a4 0b }
len=1   offset=d9,{ 0f }
len=1   offset=e4,{ 08 }
Analyzing rtl8723d_config.dms
Signature:   0x8723ab55
Data length: 41
len=1   offset=f4,{ 01 }
len=2   offset=f6,{ 81 00 }
len=2   offset=fa,{ 12 80 }
len=16  offset=0c,{ 02 80 92 04 50 c5 ea 19 e1 1b fd af 5f 01 a4 0b }
len=1   offset=d9,{ 0f }
len=1   offset=e4,{ 08 }
Analyzing rtl8822b_config.bin
Signature:   0x8723ab55
Data length: 8

len=1 offset=d9,{ 0f }
len=1 offset=e4,{ 08 }

Quoted 3 lines
Quoted 3 lines

The first two are some UART based ones and the last one is USB based.

So the 0x3c offset seems to be the BD_ADDR and 0x0c offset is the UART configuration. It would be good to know which settings the other ones control.

this matches my findings from the hciattach_rtk tool for rtl8723bs_bt and rtl8723ds_bt

Quoted 9 lines
Quoted 9 lines

Also the 16 octet UART config blob seems to be decoded like this:

     uart_config {
             le32 baudrate;
             u8[8] reserved1;
             u8 flowctl;
             u8[3] reserved2;
     }

I could not find this in any of the rtl8723bs_bt or rtl8723ds_bt sources so thank you for sharing this (even though this descriptin is missing a few bytes)!

Quoted 5 lines
Quoted 1 line
Quoted 1 line

Actually hciattach_rtk just takes the baud rate and and hardware flow control bit out of this file. That is clearly two things that are better written in plain text in the DT file.

this matches my findings apart from that hciattach_rtk also appends the config blob to the "firmware patch" that is being uploaded to the device
if you are brave you can have a look at [0]

so this makes kinda sense. If you do not load the config file, you end up with the ROM defaults for all their ?efuse? settings. Since it is essentially just a memory area where the config file overwrites defaults, this is all fine.

They have to extract the UART settings out of it since they also need to handled special during the firmware loading / boot process. This is also means if there is no config file or not UART settings in the config file, the process should use the default values.

Quoted 19 lines
Quoted 19 lines

so this is actually some funny stuff if you start to understand it.

Analyzing rtl8723b_config.dms

Signature: 0x8723ab55
Data len:  38
len=8   offset=00f4,{ 01 00 00 00 05 50 00 00 }
len=16  offset=000c,{ 02 80 92 04 50 c5 ea 19 e1 1b f1 af 5f 01 a4 0b },UART_CONFIG
len=1   offset=0027,{ 63 }
len=1   offset=00fe,{ 01 }
Analyzing rtl8723d_config_1000000_noflow.dms
Signature: 0x8723ab55
Data len:  41
len=1   offset=00f4,{ 01 }
len=2   offset=00f6,{ 81 00 }
len=2   offset=00fa,{ 12 80 }
len=16  offset=000c,{ 04 50 00 00 50 c5 ea 19 e1 1b fd af 5b 01 a4 0b },UART_CONFIG
len=1   offset=00d9,{ 0f }
len=1   offset=00e4,{ 08 }
Quoted 3 lines
Quoted 1 line

Seems like Realtek really defines memory offsets in this file and they can be defined in various different ways.

wow, that is interesting - I was wondering why they called it "offset" instead of "config_id" (or something similar)

It makes sense, they are just ?patching? their configuration space.

Quoted 10 lines
Quoted 5 lines

So 00f4,{ 01 00 00 00 05 50 00 00 } defines the whole PCM settings for interface 1 and 2. And 00f4,{ 01 } + 00f6,{ 81 00 } + 00fa,{ 12 80 } is the same PCM settings, but with only pieces of it defined.

This also means a 000c,{ 04 50 00 00 50 } for just setting the baud rate is as valid if flow control defaults to off and all other values are actual defaults. So code inside hciattach_rtk.c is also kind faulty on how it handles the flow control bit. It works if the config files all have offset 000c in it, but if not, then they are going funky.

Since these are efuse settings, I really wonder if there is just a HCI vendor command to read out the defaults and we use that to compare. And what I would really like to know is what these settings are suppose to change. Since even for USB, we are actually not even applying them.

the idea with the vendor command to read out existing memory is interesting

based on the code in "btrtl.c" it seems that we are applying the settings from the config blob.
we are simply appending it at the end of the "firmware patch", see [1]

I am pretty sure it is all the same for USB and UART.

Quoted 10 lines
Quoted 1 line

Anyway, I am certain that for Realtek UART devices, we just want to specify max-speed DT property like we do with the others. And then maybe a flow-control DT property to control that one (following what nfcmrvl.txt does). We can use the rtlfw tool that I wrote to extract the values from Realtek provided config files. Frankly the PCM settings we have to deal with as well at some point. But that is also missing for Broadcom and others.

just to make sure I understand you correctly: would you generate the config blob in-memory (in a function in btrtl.c) and hardcode all unknown bits (the reserved bits in the UART config entry for example) or would you mandate that a config blob is present (so request_firmware can fetch it) for "high-speed" operation?

for PCM: I cannot test that anyways since the Amlogic platform does not have audio support yet

Quoted 8 lines
Quoted 1 line

Also if there is no config file, we should be able to just assume no flow control, 115200 baud rate and H:5 as protocol. That means that almost all chips will just work. They are just slow since we do not deal with the max-speed setting.

my problem so far was that uploading the firmware patch without appending a config blob (see [1]) broke the UART communication (it was not 115200 baud as before, and I also tried other speeds such as 1500000 and 1000000 baud - neither worked)
however, extracting the existing values from the efuse using a vendor command might give a hint why (maybe they are fallling back to some exotic baudrate in that case, etc?)

Have you tried to just create a config file with just the signature 0x8723ab55 and data len set to 0. So essentially an empty config file. I am curious if such a default config is good enough to get things working. Or if they have actually faulty defaults in their ROM that need overwriting to make things fly.

Btw. I am curious why the UART speed is in that file anyway if we have to change it anyway via HCI command. So normally the boot UART speed is what is needed to know, not what final high speed we are using. So this is a bit odd.

I wonder if just loading the config file if present and then use a DT property max-speed to select the speed (even if it is different from the config file).

For me this means that we can have a general config file on disk, but actually allow overwriting the UART speed via DT. Otherwise we might just want to add extra offset entries for the config blob loading from disk and extend it with the DT values. So it would be some sort of overload.

Regards

Marcel

author reviewer
#31 lore raw

RFC v2 8/9 Bluetooth: drop HCI_UART_INIT_PENDING support

In reply to Martin Blumenstingl

The three-wire (H5) protocol is the only protocol which uses HCI_UART_INIT_PENDING.
Unfortunately the benefits of using this flag are currently unknown. It was added in commit 9f2aee848fe6 ("Bluetooth: Add delayed init sequence support for UART controllers"). In my experiments (with the "rtk_hciattach" tool - a customized version of hciattach for Realtek chipsets) I started the tool before and after this patch while the Bluetooth chipset was disabled (by pulling it's enable GPIO LOW). In both cases hci0 was not created - thus HCI_UART_INIT_PENDING is not required in that case.

Removing this code also has another benefit: hci_serdev.c does not support the delayed initialization / registration. Thus the protocol implementation (hci_h5) never receives any data with this check still in place. For the H5 protocol this means that the initialization never completes (because the sync response never arrives). Even if the initialization would succeed later on the drivers would call hci_uart_init_ready() which schedules the registration which is currently not implemented by hci_serdev.c.

Removing the HCI_UART_INIT_PENDING check makes the code easier to read and also fixes the initalization of devices (implemented with the serdev library) which use the H5 protocol.

---
 drivers/bluetooth/hci_h5.c     |  3 ---
 drivers/bluetooth/hci_ldisc.c  | 38 --------------------------------------
 drivers/bluetooth/hci_serdev.c |  3 ---
 drivers/bluetooth/hci_uart.h   |  4 +---
 4 files changed, 1 insertion(+), 47 deletions(-)
drivers/bluetooth/hci_h5.c +0 -3
index 6a8d0d06aba7..6cfc2f276250 100644
@@ -210,8 +210,6 @@ static int h5_open(struct hci_uart *hu)
 
 	h5->tx_win = H5_TX_WIN_MAX;
 
-	set_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags);
-
 	/* Send initial sync request */
 	h5_link_control(hu, sync, sizeof(sync));
 	mod_timer(&h5->timer, jiffies + H5_SYNC_TIMEOUT);
@@ -316,7 +314,6 @@ static void h5_handle_internal_rx(struct hci_uart *hu)
 			h5->tx_win = (data[2] & 0x07);
 		BT_DBG("Three-wire init complete. tx_win %u", h5->tx_win);
 		h5->state = H5_ACTIVE;
-		hci_uart_init_ready(hu);
 		return;
 	} else if (memcmp(data, sleep_req, 2) == 0) {
 		BT_DBG("Peer went to sleep");
drivers/bluetooth/hci_ldisc.c +0 -38
index c823914b3a80..5dd3e1bebfe4 100644
@@ -195,39 +195,6 @@ static void hci_uart_write_work(struct work_struct *work)
 	clear_bit(HCI_UART_SENDING, &hu->tx_state);
 }
 
-static void hci_uart_init_work(struct work_struct *work)
-{
-	struct hci_uart *hu = container_of(work, struct hci_uart, init_ready);
-	int err;
-	struct hci_dev *hdev;
-
-	if (!test_and_clear_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags))
-		return;
-
-	err = hci_register_dev(hu->hdev);
-	if (err < 0) {
-		BT_ERR("Can't register HCI device");
-		hdev = hu->hdev;
-		hu->hdev = NULL;
-		hci_free_dev(hdev);
-		clear_bit(HCI_UART_PROTO_READY, &hu->flags);
-		hu->proto->close(hu);
-		return;
-	}
-
-	set_bit(HCI_UART_REGISTERED, &hu->flags);
-}
-
-int hci_uart_init_ready(struct hci_uart *hu)
-{
-	if (!test_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags))
-		return -EALREADY;
-
-	schedule_work(&hu->init_ready);
-
-	return 0;
-}
-
 /* ------- Interface to HCI layer ------ */
 /* Initialize device */
 static int hci_uart_open(struct hci_dev *hdev)
@@ -490,7 +457,6 @@ static int hci_uart_tty_open(struct tty_struct *tty)
 	hu->alignment = 1;
 	hu->padding = 0;
 
-	INIT_WORK(&hu->init_ready, hci_uart_init_work);
 	INIT_WORK(&hu->write_work, hci_uart_write_work);
 
 	percpu_init_rwsem(&hu->proto_lock);
@@ -653,9 +619,6 @@ static int hci_uart_register_dev(struct hci_uart *hu)
 	else
 		hdev->dev_type = HCI_PRIMARY;
 
-	if (test_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags))
-		return 0;
-
 	if (hci_register_dev(hdev) < 0) {
 		BT_ERR("Can't register HCI device");
 		hu->hdev = NULL;
@@ -699,7 +662,6 @@ static int hci_uart_set_flags(struct hci_uart *hu, unsigned long flags)
 	unsigned long valid_flags = BIT(HCI_UART_RAW_DEVICE) |
 				    BIT(HCI_UART_RESET_ON_INIT) |
 				    BIT(HCI_UART_CREATE_AMP) |
-				    BIT(HCI_UART_INIT_PENDING) |
 				    BIT(HCI_UART_EXT_CONFIG) |
 				    BIT(HCI_UART_VND_DETECT);
 
drivers/bluetooth/hci_serdev.c +0 -3
index e0e6461b9200..fe67eb6d4278 100644
@@ -333,9 +333,6 @@ int hci_uart_register_device(struct hci_uart *hu,
 	else
 		hdev->dev_type = HCI_PRIMARY;
 
-	if (test_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags))
-		return 0;
-
 	if (hci_register_dev(hdev) < 0) {
 		BT_ERR("Can't register HCI device");
 		err = -ENODEV;
drivers/bluetooth/hci_uart.h +1 -3
index 66e8c68e4607..47e755ff4092 100644
@@ -53,7 +53,7 @@
 #define HCI_UART_RAW_DEVICE	0
 #define HCI_UART_RESET_ON_INIT	1
 #define HCI_UART_CREATE_AMP	2
-#define HCI_UART_INIT_PENDING	3
+/* 3 is unused - was HCI_UART_INIT_PENDING */
 #define HCI_UART_EXT_CONFIG	4
 #define HCI_UART_VND_DETECT	5
 
@@ -83,7 +83,6 @@ struct hci_uart {
 	unsigned long		flags;
 	unsigned long		hdev_flags;
 
-	struct work_struct	init_ready;
 	struct work_struct	write_work;
 
 	const struct hci_uart_proto *proto;
@@ -115,7 +114,6 @@ int hci_uart_register_device(struct hci_uart *hu, const struct hci_uart_proto *p
 void hci_uart_unregister_device(struct hci_uart *hu);
 
 int hci_uart_tx_wakeup(struct hci_uart *hu);
-int hci_uart_init_ready(struct hci_uart *hu);
 void hci_uart_set_baudrate(struct hci_uart *hu, unsigned int speed);
 void hci_uart_set_flow_control(struct hci_uart *hu, bool enable);
 void hci_uart_set_speeds(struct hci_uart *hu, unsigned int init_speed,
Signature · 1 line
2.15.1
maintainer
#32 lore raw

RFC v2 8/9 Bluetooth: drop HCI_UART_INIT_PENDING support

Hi Martin,

Quoted 20 lines

The three-wire (H5) protocol is the only protocol which uses HCI_UART_INIT_PENDING.
Unfortunately the benefits of using this flag are currently unknown. It was added in commit 9f2aee848fe6 ("Bluetooth: Add delayed init sequence support for UART controllers"). In my experiments (with the "rtk_hciattach" tool - a customized version of hciattach for Realtek chipsets) I started the tool before and after this patch while the Bluetooth chipset was disabled (by pulling it's enable GPIO LOW). In both cases hci0 was not created - thus HCI_UART_INIT_PENDING is not required in that case.

Removing this code also has another benefit: hci_serdev.c does not support the delayed initialization / registration. Thus the protocol implementation (hci_h5) never receives any data with this check still in place. For the H5 protocol this means that the initialization never completes (because the sync response never arrives). Even if the initialization would succeed later on the drivers would call hci_uart_init_ready() which schedules the registration which is currently not implemented by hci_serdev.c.

Quoted 5 lines

Removing the HCI_UART_INIT_PENDING check makes the code easier to read and also fixes the initalization of devices (implemented with the serdev library) which use the H5 protocol.

I really like Johan to ack this one, but generally I am fine with removing unneeded code.

We might also want to look at hciattach to btattach code and make sure it gets removed there as well. I am not even sure anybody used hciattach with H:5 ever.

Quoted 117 lines

---

drivers/bluetooth/hci_h5.c     |  3 ---
drivers/bluetooth/hci_ldisc.c  | 38 --------------------------------------
drivers/bluetooth/hci_serdev.c |  3 ---
drivers/bluetooth/hci_uart.h   |  4 +---
4 files changed, 1 insertion(+), 47 deletions(-)
drivers/bluetooth/hci_h5.c +0 -3
index 6a8d0d06aba7..6cfc2f276250 100644
@@ -210,8 +210,6 @@ static int h5_open(struct hci_uart *hu)
 
 	h5->tx_win = H5_TX_WIN_MAX;
 
-	set_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags);
-
 	/* Send initial sync request */
 	h5_link_control(hu, sync, sizeof(sync));
 	mod_timer(&h5->timer, jiffies + H5_SYNC_TIMEOUT);
@@ -316,7 +314,6 @@ static void h5_handle_internal_rx(struct hci_uart *hu)
 			h5->tx_win = (data[2] & 0x07);
 		BT_DBG("Three-wire init complete. tx_win %u", h5->tx_win);
 		h5->state = H5_ACTIVE;
-		hci_uart_init_ready(hu);
 		return;
 	} else if (memcmp(data, sleep_req, 2) == 0) {
 		BT_DBG("Peer went to sleep");
drivers/bluetooth/hci_ldisc.c +0 -38
index c823914b3a80..5dd3e1bebfe4 100644
@@ -195,39 +195,6 @@ static void hci_uart_write_work(struct work_struct *work)
 	clear_bit(HCI_UART_SENDING, &hu->tx_state);
 }
 
-static void hci_uart_init_work(struct work_struct *work)
-{
-	struct hci_uart *hu = container_of(work, struct hci_uart, init_ready);
-	int err;
-	struct hci_dev *hdev;
-
-	if (!test_and_clear_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags))
-		return;
-
-	err = hci_register_dev(hu->hdev);
-	if (err < 0) {
-		BT_ERR("Can't register HCI device");
-		hdev = hu->hdev;
-		hu->hdev = NULL;
-		hci_free_dev(hdev);
-		clear_bit(HCI_UART_PROTO_READY, &hu->flags);
-		hu->proto->close(hu);
-		return;
-	}
-
-	set_bit(HCI_UART_REGISTERED, &hu->flags);
-}
-
-int hci_uart_init_ready(struct hci_uart *hu)
-{
-	if (!test_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags))
-		return -EALREADY;
-
-	schedule_work(&hu->init_ready);
-
-	return 0;
-}
-
 /* ------- Interface to HCI layer ------ */
 /* Initialize device */
 static int hci_uart_open(struct hci_dev *hdev)
@@ -490,7 +457,6 @@ static int hci_uart_tty_open(struct tty_struct *tty)
 	hu->alignment = 1;
 	hu->padding = 0;
 
-	INIT_WORK(&hu->init_ready, hci_uart_init_work);
 	INIT_WORK(&hu->write_work, hci_uart_write_work);
 
 	percpu_init_rwsem(&hu->proto_lock);
@@ -653,9 +619,6 @@ static int hci_uart_register_dev(struct hci_uart *hu)
 	else
 		hdev->dev_type = HCI_PRIMARY;
 
-	if (test_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags))
-		return 0;
-
 	if (hci_register_dev(hdev) < 0) {
 		BT_ERR("Can't register HCI device");
 		hu->hdev = NULL;
@@ -699,7 +662,6 @@ static int hci_uart_set_flags(struct hci_uart *hu, unsigned long flags)
 	unsigned long valid_flags = BIT(HCI_UART_RAW_DEVICE) |
 				    BIT(HCI_UART_RESET_ON_INIT) |
 				    BIT(HCI_UART_CREATE_AMP) |
-				    BIT(HCI_UART_INIT_PENDING) |
 				    BIT(HCI_UART_EXT_CONFIG) |
 				    BIT(HCI_UART_VND_DETECT);
 
drivers/bluetooth/hci_serdev.c +0 -3
index e0e6461b9200..fe67eb6d4278 100644
@@ -333,9 +333,6 @@ int hci_uart_register_device(struct hci_uart *hu,
 	else
 		hdev->dev_type = HCI_PRIMARY;
 
-	if (test_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags))
-		return 0;
-
 	if (hci_register_dev(hdev) < 0) {
 		BT_ERR("Can't register HCI device");
 		err = -ENODEV;
drivers/bluetooth/hci_uart.h +0 -0
index 66e8c68e4607..47e755ff4092 100644

drivers/bluetooth/hci_uart.h line 56

Quoted 5 lines
drivers/bluetooth/hci_uart.h +1 -1
@@ -53,7 +53,7 @@
 #define HCI_UART_RAW_DEVICE	0
 #define HCI_UART_RESET_ON_INIT	1
 #define HCI_UART_CREATE_AMP	2
-#define HCI_UART_INIT_PENDING	3
+/* 3 is unused - was HCI_UART_INIT_PENDING */

#define HCI_UART_INIT_PENDING 3 /* unused */

I prefer it this way since it is easier on the eyes.

Quoted 12 lines
drivers/bluetooth/hci_uart.h +0 -1
 #define HCI_UART_EXT_CONFIG	4
 #define HCI_UART_VND_DETECT	5
 
@@ -83,7 +83,6 @@ struct hci_uart {
 	unsigned long		flags;
 	unsigned long		hdev_flags;
 
-	struct work_struct	init_ready;
 	struct work_struct	write_work;
 
 	const struct hci_uart_proto *proto;

drivers/bluetooth/hci_uart.h int hci_uart_register_device(struct hci_uart *hu, const struct hci_uart_proto *p · line 119

Quoted 7 lines
drivers/bluetooth/hci_uart.h +0 -1
@@ -115,7 +114,6 @@ int hci_uart_register_device(struct hci_uart *hu, const struct hci_uart_proto *p
 void hci_uart_unregister_device(struct hci_uart *hu);
 
 int hci_uart_tx_wakeup(struct hci_uart *hu);
-int hci_uart_init_ready(struct hci_uart *hu);
 void hci_uart_set_baudrate(struct hci_uart *hu, unsigned int speed);
 void hci_uart_set_flow_control(struct hci_uart *hu, bool enable);
 void hci_uart_set_speeds(struct hci_uart *hu, unsigned int init_speed,

Regards

Marcel

author reviewer
#33 lore raw

RFC v2 8/9 Bluetooth: drop HCI_UART_INIT_PENDING support

Hi Marcel,

thank you for looking into this latest version!

Quoted 22 lines · Marcel Holtmann

Hi Martin,

Quoted 20 lines

The three-wire (H5) protocol is the only protocol which uses HCI_UART_INIT_PENDING.
Unfortunately the benefits of using this flag are currently unknown. It was added in commit 9f2aee848fe6 ("Bluetooth: Add delayed init sequence support for UART controllers"). In my experiments (with the "rtk_hciattach" tool - a customized version of hciattach for Realtek chipsets) I started the tool before and after this patch while the Bluetooth chipset was disabled (by pulling it's enable GPIO LOW). In both cases hci0 was not created - thus HCI_UART_INIT_PENDING is not required in that case.

Removing this code also has another benefit: hci_serdev.c does not support the delayed initialization / registration. Thus the protocol implementation (hci_h5) never receives any data with this check still in place. For the H5 protocol this means that the initialization never completes (because the sync response never arrives). Even if the initialization would succeed later on the drivers would call hci_uart_init_ready() which schedules the registration which is currently not implemented by hci_serdev.c.

Quoted 7 lines
Quoted 5 lines

Removing the HCI_UART_INIT_PENDING check makes the code easier to read and also fixes the initalization of devices (implemented with the serdev library) which use the H5 protocol.

I really like Johan to ack this one, but generally I am fine with removing unneeded code.

I would also like as many ACKs/Tested-by/Reviewed-by as possible since this is all new code for me (so it's easy for me to make mistakes)!

Quoted 1 line

We might also want to look at hciattach to btattach code and make sure it gets removed there as well. I am not even sure anybody used hciattach with H:5 ever.

a quick glance shows that it's defined in bluez.git but never used there (which is good in this case)

Quoted 125 lines
Quoted 117 lines

---

drivers/bluetooth/hci_h5.c     |  3 ---
drivers/bluetooth/hci_ldisc.c  | 38 --------------------------------------
drivers/bluetooth/hci_serdev.c |  3 ---
drivers/bluetooth/hci_uart.h   |  4 +---
4 files changed, 1 insertion(+), 47 deletions(-)
drivers/bluetooth/hci_h5.c +0 -3
index 6a8d0d06aba7..6cfc2f276250 100644
@@ -210,8 +210,6 @@ static int h5_open(struct hci_uart *hu)
 
      h5->tx_win = H5_TX_WIN_MAX;
 
-     set_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags);
-
      /* Send initial sync request */
      h5_link_control(hu, sync, sizeof(sync));
      mod_timer(&h5->timer, jiffies + H5_SYNC_TIMEOUT);
@@ -316,7 +314,6 @@ static void h5_handle_internal_rx(struct hci_uart *hu)
                      h5->tx_win = (data[2] & 0x07);
              BT_DBG("Three-wire init complete. tx_win %u", h5->tx_win);
              h5->state = H5_ACTIVE;
-             hci_uart_init_ready(hu);
              return;
      } else if (memcmp(data, sleep_req, 2) == 0) {
              BT_DBG("Peer went to sleep");
drivers/bluetooth/hci_ldisc.c +0 -38
index c823914b3a80..5dd3e1bebfe4 100644
@@ -195,39 +195,6 @@ static void hci_uart_write_work(struct work_struct *work)
      clear_bit(HCI_UART_SENDING, &hu->tx_state);
 }
 
-static void hci_uart_init_work(struct work_struct *work)
-{
-     struct hci_uart *hu = container_of(work, struct hci_uart, init_ready);
-     int err;
-     struct hci_dev *hdev;
-
-     if (!test_and_clear_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags))
-             return;
-
-     err = hci_register_dev(hu->hdev);
-     if (err < 0) {
-             BT_ERR("Can't register HCI device");
-             hdev = hu->hdev;
-             hu->hdev = NULL;
-             hci_free_dev(hdev);
-             clear_bit(HCI_UART_PROTO_READY, &hu->flags);
-             hu->proto->close(hu);
-             return;
-     }
-
-     set_bit(HCI_UART_REGISTERED, &hu->flags);
-}
-
-int hci_uart_init_ready(struct hci_uart *hu)
-{
-     if (!test_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags))
-             return -EALREADY;
-
-     schedule_work(&hu->init_ready);
-
-     return 0;
-}
-
 /* ------- Interface to HCI layer ------ */
 /* Initialize device */
 static int hci_uart_open(struct hci_dev *hdev)
@@ -490,7 +457,6 @@ static int hci_uart_tty_open(struct tty_struct *tty)
      hu->alignment = 1;
      hu->padding = 0;
 
-     INIT_WORK(&hu->init_ready, hci_uart_init_work);
      INIT_WORK(&hu->write_work, hci_uart_write_work);
 
      percpu_init_rwsem(&hu->proto_lock);
@@ -653,9 +619,6 @@ static int hci_uart_register_dev(struct hci_uart *hu)
      else
              hdev->dev_type = HCI_PRIMARY;
 
-     if (test_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags))
-             return 0;
-
      if (hci_register_dev(hdev) < 0) {
              BT_ERR("Can't register HCI device");
              hu->hdev = NULL;
@@ -699,7 +662,6 @@ static int hci_uart_set_flags(struct hci_uart *hu, unsigned long flags)
      unsigned long valid_flags = BIT(HCI_UART_RAW_DEVICE) |
                                  BIT(HCI_UART_RESET_ON_INIT) |
                                  BIT(HCI_UART_CREATE_AMP) |
-                                 BIT(HCI_UART_INIT_PENDING) |
                                  BIT(HCI_UART_EXT_CONFIG) |
                                  BIT(HCI_UART_VND_DETECT);
 
drivers/bluetooth/hci_serdev.c +0 -3
index e0e6461b9200..fe67eb6d4278 100644
@@ -333,9 +333,6 @@ int hci_uart_register_device(struct hci_uart *hu,
      else
              hdev->dev_type = HCI_PRIMARY;
 
-     if (test_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags))
-             return 0;
-
      if (hci_register_dev(hdev) < 0) {
              BT_ERR("Can't register HCI device");
              err = -ENODEV;
drivers/bluetooth/hci_uart.h +0 -0
index 66e8c68e4607..47e755ff4092 100644

drivers/bluetooth/hci_uart.h line 56

Quoted 5 lines
drivers/bluetooth/hci_uart.h +1 -1
@@ -53,7 +53,7 @@
 #define HCI_UART_RAW_DEVICE   0
 #define HCI_UART_RESET_ON_INIT        1
 #define HCI_UART_CREATE_AMP   2
-#define HCI_UART_INIT_PENDING        3
+/* 3 is unused - was HCI_UART_INIT_PENDING */

#define HCI_UART_INIT_PENDING 3 /* unused */

Quoted 1 line

I prefer it this way since it is easier on the eyes.

OK, I'll do it that way in the next version

Quoted 14 lines
Quoted 14 lines
drivers/bluetooth/hci_uart.h +0 -1
 #define HCI_UART_EXT_CONFIG   4
 #define HCI_UART_VND_DETECT   5
 
@@ -83,7 +83,6 @@ struct hci_uart {
      unsigned long           flags;
      unsigned long           hdev_flags;
 
-     struct work_struct      init_ready;
      struct work_struct      write_work;
 
      const struct hci_uart_proto *proto;
@@ -115,7 +114,6 @@ int hci_uart_register_device(struct hci_uart *hu, const struct hci_uart_proto *p
 void hci_uart_unregister_device(struct hci_uart *hu);
 
Quoted 5 lines

drivers/bluetooth/hci_uart.h int hci_uart_register_device(struct hci_uart *hu, const struct hci_uart_proto *p · line 119

Quoted 5 lines
drivers/bluetooth/hci_uart.h +0 -1
 int hci_uart_tx_wakeup(struct hci_uart *hu);
-int hci_uart_init_ready(struct hci_uart *hu);
 void hci_uart_set_baudrate(struct hci_uart *hu, unsigned int speed);
 void hci_uart_set_flow_control(struct hci_uart *hu, bool enable);
 void hci_uart_set_speeds(struct hci_uart *hu, unsigned int init_speed,

Regards
Martin

#34 lore raw

RFC v2 8/9 Bluetooth: drop HCI_UART_INIT_PENDING support

Hi Martin,

Quoted 32 lines · Martin Blumenstingl

Hi Marcel,

thank you for looking into this latest version!

Quoted 27 lines · Marcel Holtmann

Hi Martin,

Quoted 25 lines

The three-wire (H5) protocol is the only protocol which uses HCI_UART_INIT_PENDING.
Unfortunately the benefits of using this flag are currently unknown. It was added in commit 9f2aee848fe6 ("Bluetooth: Add delayed init sequence support for UART controllers"). In my experiments (with the "rtk_hciattach" tool - a customized version of hciattach for Realtek chipsets) I started the tool before and after this patch while the Bluetooth chipset was disabled (by pulling it's enable GPIO LOW). In both cases hci0 was not created - thus HCI_UART_INIT_PENDING is not required in that case.

Removing this code also has another benefit: hci_serdev.c does not support the delayed initialization / registration. Thus the protocol implementation (hci_h5) never receives any data with this check still in place. For the H5 protocol this means that the initialization never completes (because the sync response never arrives). Even if the initialization would succeed later on the drivers would call hci_uart_init_ready() which schedules the registration which is currently not implemented by hci_serdev.c.

Removing the HCI_UART_INIT_PENDING check makes the code easier to read and also fixes the initalization of devices (implemented with the serdev library) which use the H5 protocol.

I think the original goal is to perform H5 init peacefully. The H5 protocol needs to be open in order to send/receive H5 link packets during the H5 initialization/synchronization step. During this stage, driver prevents upper stack to send any HCI packet by delaying the HCI device registration.

Regards,
Loic

author reviewer
#35 lore raw

RFC v2 8/9 Bluetooth: drop HCI_UART_INIT_PENDING support

Hi Loic,

Quoted 35 lines · Loic Poulain

Hi Martin,

Quoted 31 lines · Martin Blumenstingl

Hi Marcel,

thank you for looking into this latest version!

Quoted 26 lines · Marcel Holtmann

Hi Martin,

Quoted 24 lines

The three-wire (H5) protocol is the only protocol which uses HCI_UART_INIT_PENDING.
Unfortunately the benefits of using this flag are currently unknown. It was added in commit 9f2aee848fe6 ("Bluetooth: Add delayed init sequence support for UART controllers"). In my experiments (with the "rtk_hciattach" tool - a customized version of hciattach for Realtek chipsets) I started the tool before and after this patch while the Bluetooth chipset was disabled (by pulling it's enable GPIO LOW). In both cases hci0 was not created - thus HCI_UART_INIT_PENDING is not required in that case.

Removing this code also has another benefit: hci_serdev.c does not support the delayed initialization / registration. Thus the protocol implementation (hci_h5) never receives any data with this check still in place. For the H5 protocol this means that the initialization never completes (because the sync response never arrives). Even if the initialization would succeed later on the drivers would call hci_uart_init_ready() which schedules the registration which is currently not implemented by hci_serdev.c.

Removing the HCI_UART_INIT_PENDING check makes the code easier to read and also fixes the initalization of devices (implemented with the serdev library) which use the H5 protocol.

Quoted 7 lines
Quoted 1 line
Quoted 1 line
Quoted 1 line

I think the original goal is to perform H5 init peacefully. The H5 protocol needs to be open in order to send/receive H5 link packets during the H5 initialization/synchronization step. During this stage, driver prevents upper stack to send any HCI packet by delaying the HCI device registration.

thank you for this explanation
do you have a test-case (manual list of steps, a scripts, etc.) which I can use to compare the old and new behavior?

based on your explanation this would mean that we should add HCI_UART_INIT_PENDING support to the serdev code rather than removing it

Regards
Martin

#36 lore raw

RFC v2 8/9 Bluetooth: drop HCI_UART_INIT_PENDING support

In reply to Martin Blumenstingl
Quoted 2 lines · Martin Blumenstingl

The three-wire (H5) protocol is the only protocol which uses HCI_UART_INIT_PENDING.

I think I'd also found that this flag wasn't really needed.

Quoted 11 lines

Unfortunately the benefits of using this flag are currently unknown. It was added in commit 9f2aee848fe6 ("Bluetooth: Add delayed init sequence support for UART controllers"). In my experiments (with the "rtk_hciattach" tool - a customized version of hciattach for Realtek chipsets) I started the tool before and after this patch while the Bluetooth chipset was disabled (by pulling it's enable GPIO LOW). In both cases hci0 was not created - thus HCI_UART_INIT_PENDING is not required in that case.

Removing this code also has another benefit: hci_serdev.c does not support the delayed initialization / registration.

This statement is misleading. serdev *always* supports async init as it forces async probe of drivers. It doesn't need to support the private workq init mechanism. At least that was my intent.

Quoted 19 lines

Thus the protocol
implementation (hci_h5) never receives any data with this check still in place. For the H5 protocol this means that the initialization never completes (because the sync response never arrives). Even if the initialization would succeed later on the drivers would call hci_uart_init_ready() which schedules the registration which is currently not implemented by hci_serdev.c.

Removing the HCI_UART_INIT_PENDING check makes the code easier to read and also fixes the initalization of devices (implemented with the serdev library) which use the H5 protocol.

---
 drivers/bluetooth/hci_h5.c     |  3 ---
 drivers/bluetooth/hci_ldisc.c  | 38 --------------------------------------
 drivers/bluetooth/hci_serdev.c |  3 ---
 drivers/bluetooth/hci_uart.h   |  4 +---
 4 files changed, 1 insertion(+), 47 deletions(-)
author reviewer
#37 lore raw

RFC v2 8/9 Bluetooth: drop HCI_UART_INIT_PENDING support

Hi Rob,

Quoted 6 lines · Rob Herring
Quoted 2 lines · Martin Blumenstingl

The three-wire (H5) protocol is the only protocol which uses HCI_UART_INIT_PENDING.

I think I'd also found that this flag wasn't really needed.

do you remember how you tested this? did you test it with you new serdev code or with the old hci_ldisc code (with some userspace tool)?

Quoted 9 lines
Quoted 9 lines

Unfortunately the benefits of using this flag are currently unknown. It was added in commit 9f2aee848fe6 ("Bluetooth: Add delayed init sequence support for UART controllers"). In my experiments (with the "rtk_hciattach" tool - a customized version of hciattach for Realtek chipsets) I started the tool before and after this patch while the Bluetooth chipset was disabled (by pulling it's enable GPIO LOW). In both cases hci0 was not created - thus HCI_UART_INIT_PENDING is not required in that case.

Quoted 6 lines
Quoted 2 lines

Removing this code also has another benefit: hci_serdev.c does not support the delayed initialization / registration.

This statement is misleading. serdev *always* supports async init as it forces async probe of drivers. It doesn't need to support the private workq init mechanism. At least that was my intent.

thank you for having a look at this patch!
I will update the description in the next version

Quoted 12 lines
Quoted 12 lines

Thus the protocol
implementation (hci_h5) never receives any data with this check still in place. For the H5 protocol this means that the initialization never completes (because the sync response never arrives). Even if the initialization would succeed later on the drivers would call hci_uart_init_ready() which schedules the registration which is currently not implemented by hci_serdev.c.

Removing the HCI_UART_INIT_PENDING check makes the code easier to read and also fixes the initalization of devices (implemented with the serdev library) which use the H5 protocol.

Quoted 7 lines
Quoted 7 lines
---
 drivers/bluetooth/hci_h5.c     |  3 ---
 drivers/bluetooth/hci_ldisc.c  | 38 --------------------------------------
 drivers/bluetooth/hci_serdev.c |  3 ---
 drivers/bluetooth/hci_uart.h   |  4 +---
 4 files changed, 1 insertion(+), 47 deletions(-)

Regards
Martin

author reviewer
#38 lore raw

RFC v2 9/9 Bluetooth: hci_h5: add support for Realtek UART Bluetooth modules

In reply to Martin Blumenstingl

Realtek RTL8723BS and RTL8723DS are SDIO wifi chips with an embedded Bluetooth controller which connects to the host via UART. The H5 protocol is used for communication between host and device.

The Realtek "rtl8723bs_bt" and "rtl8723ds_bt" userspace Bluetooth UART initialization tools (rtk_hciattach) use the following sequence:
1) send H5 sync pattern (already supported by hci_h5)
2) get LMP version (already supported by btrtl)
3) get ROM version (already supported by btrtl)
4) load the firmware and config for the current chipset (already supported by btrtl)
5) read UART settings from the config blob (already supported by btrtl)
6) send UART settings via a vendor command to the device (which changes the baudrate of the device and enables or disables flow control depending on the config)
7) change the baudrate and flow control settings on the host
8) send the firmware and config blob to the device (already supported by btrtl)

This uses the serdev library as well as the existing btrtl driver to initialize the Bluetooth functionality, which consists of:
- identifying the device and loading the corresponding firmware and config blobs (steps #2, #3 and #4)
- configuring the baudrate and flow control (steps #6 and #7)
- uploading the firmware to the device (step #8)

---
 drivers/bluetooth/Kconfig  |   1 +
 drivers/bluetooth/hci_h5.c | 205 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 206 insertions(+)
drivers/bluetooth/Kconfig +1 -0
index 60e1c7d6986d..3001f1200c72 100644
@@ -146,6 +146,7 @@ config BT_HCIUART_LL
 config BT_HCIUART_3WIRE
 	bool "Three-wire UART (H5) protocol support"
 	depends on BT_HCIUART
+	select BT_RTL if SERIAL_DEV_BUS
 	help
 	  The HCI Three-wire UART Transport Layer makes it possible to
 	  user the Bluetooth HCI over a serial port interface. The HCI
drivers/bluetooth/hci_h5.c +205 -0

262 lines not on this page · raw message

Signature · 1 line
2.15.1
maintainer
#39 lore raw

RFC v2 9/9 Bluetooth: hci_h5: add support for Realtek UART Bluetooth modules

Hi Martin,

Quoted 41 lines

Realtek RTL8723BS and RTL8723DS are SDIO wifi chips with an embedded Bluetooth controller which connects to the host via UART. The H5 protocol is used for communication between host and device.

The Realtek "rtl8723bs_bt" and "rtl8723ds_bt" userspace Bluetooth UART initialization tools (rtk_hciattach) use the following sequence:
1) send H5 sync pattern (already supported by hci_h5)
2) get LMP version (already supported by btrtl)
3) get ROM version (already supported by btrtl)
4) load the firmware and config for the current chipset (already supported by btrtl)
5) read UART settings from the config blob (already supported by btrtl)
6) send UART settings via a vendor command to the device (which changes the baudrate of the device and enables or disables flow control depending on the config)
7) change the baudrate and flow control settings on the host
8) send the firmware and config blob to the device (already supported by btrtl)

This uses the serdev library as well as the existing btrtl driver to initialize the Bluetooth functionality, which consists of:
- identifying the device and loading the corresponding firmware and config blobs (steps #2, #3 and #4)
- configuring the baudrate and flow control (steps #6 and #7)
- uploading the firmware to the device (step #8)

---
drivers/bluetooth/Kconfig | 1 +
drivers/bluetooth/hci_h5.c | 205 +++++++++++++++++++++++++++++++++++++++++++++

2 files changed, 206 insertions(+)
drivers/bluetooth/Kconfig +0 -0
index 60e1c7d6986d..3001f1200c72 100644
@@ -146,6 +146,7 @@ config BT_HCIUART_LL

config BT_HCIUART_3WIRE
bool "Three-wire UART (H5) protocol support" depends on BT_HCIUART

drivers/bluetooth/Kconfig +1 -0
+	select BT_RTL if SERIAL_DEV_BUS
Quoted 3 lines

help
The HCI Three-wire UART Transport Layer makes it possible to user the Bluetooth HCI over a serial port interface. The HCI

while this is fine initially, I think long term this is not sustainable. So we need to abstract the H:5 part and the vendor specific setup part so that you can have a hci_rtl.c that uses H:5 instead of H:4 and still is as tiny and simple as our H:4 drivers.

One other think I am not really happy about here is the Kconfig entry itself. You need to know that you need 3WIRE and SERDEV to magically enable RTL UART support. Maybe it would be better to just have a CONFIG_HCIUART_RTL and duplicate the config entry just specific to RTL. It then can select RTL, but in the Makefile it will result in using the same hci_h5.c file.

Quoted 190 lines
drivers/bluetooth/hci_h5.c +139 -0
index 6cfc2f276250..a03acc3b1b52 100644
@@ -28,7 +28,14 @@
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/serdev.h>
+
 #include "hci_uart.h"
+#include "btrtl.h"
 
 #define HCI_3WIRE_ACK_PKT	0
 #define HCI_3WIRE_LINK_PKT	15
@@ -97,6 +104,13 @@ struct h5 {
 	} sleep;
 };
 
+struct h5_device {
+	struct hci_uart hu;
+	struct gpio_desc *enable_gpio;
+	struct gpio_desc *reset_gpio;
+	int (*vendor_setup)(struct h5_device *h5_dev);
+};
+
 static void h5_reset_rx(struct h5 *h5);
 
 static void h5_link_control(struct hci_uart *hu, const void *data, size_t len)
@@ -190,6 +204,7 @@ static int h5_open(struct hci_uart *hu)
 {
 	struct h5 *h5;
 	const unsigned char sync[] = { 0x01, 0x7e };
+	int err;
 
 	BT_DBG("hu %p", hu);
 
@@ -210,6 +225,14 @@ static int h5_open(struct hci_uart *hu)
 
 	h5->tx_win = H5_TX_WIN_MAX;
 
+	if (hu->serdev) {
+		err = serdev_device_open(hu->serdev);
+		if (err) {
+			bt_dev_err(hu->hdev, "failed to open serdev: %d", err);
+			return err;
+		}
+	}
+
 	/* Send initial sync request */
 	h5_link_control(hu, sync, sizeof(sync));
 	mod_timer(&h5->timer, jiffies + H5_SYNC_TIMEOUT);
@@ -217,6 +240,25 @@ static int h5_open(struct hci_uart *hu)
 	return 0;
 }
 
+static int h5_setup(struct hci_uart *hu)
+{
+	int err;
+	struct h5_device *h5_dev;
+
+	if (!hu->serdev)
+		return 0;
+
+	h5_dev = serdev_device_get_drvdata(hu->serdev);
+
+	if (h5_dev->vendor_setup) {
+		err = h5_dev->vendor_setup(h5_dev);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
 static int h5_close(struct hci_uart *hu)
 {
 	struct h5 *h5 = hu->priv;
@@ -227,6 +269,15 @@ static int h5_close(struct hci_uart *hu)
 	skb_queue_purge(&h5->rel);
 	skb_queue_purge(&h5->unrel);
 
+	if (hu->serdev) {
+		struct h5_device *h5_dev;
+
+		h5_dev = serdev_device_get_drvdata(hu->serdev);
+		gpiod_set_value_cansleep(h5_dev->enable_gpio, 0);
+
+		serdev_device_close(hu->serdev);
+	}
+
 	kfree(h5);
 
 	return 0;
@@ -736,10 +787,160 @@ static int h5_flush(struct hci_uart *hu)
 	return 0;
 }
 
+#if IS_ENABLED(CONFIG_SERIAL_DEV_BUS)
+static int h5_setup_realtek(struct h5_device *h5_dev)
+{
+	struct hci_uart *hu = &h5_dev->hu;
+	int err = 0, retry = 3;
+	struct sk_buff *skb;
+	struct btrtl_device_info *btrtl_dev;
+	__le32 baudrate_data;
+	u32 device_baudrate;
+	unsigned int controller_baudrate;
+	bool flow_control;
+
+	/* devices always start with flow control disabled and even parity */
+	serdev_device_set_flow_control(hu->serdev, false);
+	serdev_device_set_parity(hu->serdev, true, false);
+
+	do {
+		/* disable the device and put it into reset. some devices only
+		 * have one of these lines, so we toggle both here to support
+		 * all combinations.
+		 */
+		gpiod_set_value_cansleep(h5_dev->reset_gpio, 1);
+		gpiod_set_value_cansleep(h5_dev->enable_gpio, 0);
+
+		/* wait until the device is disabled and/or reset. 500ms are
+		 * chosen by manually testing on a RTL8723BS. shorter wait
+		 * times lead to a non-responding device.
+		 */
+		msleep(500);
+
+		/* take the device out of reset and enable it. */
+		gpiod_set_value_cansleep(h5_dev->reset_gpio, 0);
+		gpiod_set_value_cansleep(h5_dev->enable_gpio, 1);
+
+		/* after that we need to wait 500ms, otherwise the device might
+		 * not respond in all cases. this was determined by testing
+		 * with a RTL8723BS.
+		 */
+		msleep(500);
+
+		btrtl_dev = btrtl_initialize(hu->hdev);
+		if (!IS_ERR(btrtl_dev))
+			break;
+
+		/* Toggle the enable and reset pins above and try again */
+	} while (retry--);
+
+	if (IS_ERR(btrtl_dev))
+		return PTR_ERR(btrtl_dev);
+
+	err = btrtl_get_uart_settings(hu->hdev, btrtl_dev,
+				      &controller_baudrate, &device_baudrate,
+				      &flow_control);
+	if (err)
+		goto out_free;
+
+	baudrate_data = cpu_to_le32(device_baudrate);
+	skb = __hci_cmd_sync(hu->hdev, 0xfc17, sizeof(baudrate_data),
+			     &baudrate_data, HCI_INIT_TIMEOUT);
+	if (IS_ERR(skb)) {
+		bt_dev_err(hu->hdev, "set baud rate command failed");
+		err = -PTR_ERR(skb);
+		goto out_free;
+	} else {
+		kfree_skb(skb);
+	}
+
+	serdev_device_set_baudrate(hu->serdev, controller_baudrate);
+	serdev_device_set_flow_control(hu->serdev, flow_control);
+
+	err = btrtl_download_firmware(hu->hdev, btrtl_dev);
+
+out_free:
+	btrtl_free(btrtl_dev);
+
+	return err;
+}
+
+static const struct hci_uart_proto h5p;
+
+static int hci_h5_probe(struct serdev_device *serdev)
+{
+	struct hci_uart *hu;
+	struct h5_device *h5_dev;
+
+	h5_dev = devm_kzalloc(&serdev->dev, sizeof(*h5_dev), GFP_KERNEL);
+	if (!h5_dev)
+		return -ENOMEM;

drivers/bluetooth/hci_h5.c static int h5_flush(struct hci_uart *hu) · line 887

Quoted 10 lines
drivers/bluetooth/hci_h5.c +10 -0
+
+	hu = &h5_dev->hu;
+	hu->serdev = serdev;
+
+	serdev_device_set_drvdata(serdev, h5_dev);
+
+	h5_dev->vendor_setup = of_device_get_match_data(&serdev->dev);
+
+	h5_dev->enable_gpio = devm_gpiod_get_optional(&serdev->dev, "enable",
+						       GPIOD_OUT_HIGH);

Indentation mistake.

Quoted 64 lines
drivers/bluetooth/hci_h5.c +53 -0
+	if (IS_ERR(h5_dev->enable_gpio))
+		return PTR_ERR(h5_dev->enable_gpio);
+
+	h5_dev->reset_gpio = devm_gpiod_get_optional(&serdev->dev, "reset",
+						     GPIOD_OUT_LOW);
+	if (IS_ERR(h5_dev->reset_gpio))
+		return PTR_ERR(h5_dev->reset_gpio);
+
+	hci_uart_set_speeds(hu, 115200, 0);
+
+	return hci_uart_register_device(hu, &h5p);
+}
+
+static void hci_h5_remove(struct serdev_device *serdev)
+{
+	struct h5_device *h5_dev = serdev_device_get_drvdata(serdev);
+	struct hci_uart *hu = &h5_dev->hu;
+	struct hci_dev *hdev = hu->hdev;
+
+	cancel_work_sync(&hu->write_work);
+
+	hci_unregister_dev(hdev);
+	hci_free_dev(hdev);
+	hu->proto->close(hu);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id hci_h5_of_match[] = {
+	{
+		.compatible = "realtek,rtl8723bs-bluetooth",
+		.data = h5_setup_realtek
+	},
+	{
+		.compatible = "realtek,rtl8723ds-bluetooth",
+		.data = h5_setup_realtek
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, hci_h5_of_match);
+#endif
+
+static struct serdev_device_driver hci_h5_drv = {
+	.driver		= {
+		.name	= "hci-h5",
+		.of_match_table = of_match_ptr(hci_h5_of_match),
+	},
+	.probe	= hci_h5_probe,
+	.remove	= hci_h5_remove,
+};
+#endif
+
 static const struct hci_uart_proto h5p = {
 	.id		= HCI_UART_3WIRE,
 	.name		= "Three-wire (H5)",
 	.open		= h5_open,
+	.setup		= h5_setup,
 	.close		= h5_close,
 	.recv		= h5_recv,
 	.enqueue	= h5_enqueue,
@@ -749,10 +950,14 @@ static const struct hci_uart_proto h5p = {
 
 int __init h5_init(void)
 {
+	serdev_device_driver_register(&hci_h5_drv);

drivers/bluetooth/hci_h5.c static const struct hci_uart_proto h5p = { · line 963

Quoted 10 lines
drivers/bluetooth/hci_h5.c +3 -0
+
 	return hci_uart_register_proto(&h5p);
 }
 
 int __exit h5_deinit(void)
 {
+	serdev_device_driver_unregister(&hci_h5_drv);
+
 	return hci_uart_unregister_proto(&h5p);
 }

Regards

Marcel

author reviewer
#40 lore raw

RFC v2 9/9 Bluetooth: hci_h5: add support for Realtek UART Bluetooth modules

Hi Marcel,

Quoted 34 lines · Marcel Holtmann

Hi Martin,

Quoted 32 lines

Realtek RTL8723BS and RTL8723DS are SDIO wifi chips with an embedded Bluetooth controller which connects to the host via UART. The H5 protocol is used for communication between host and device.

The Realtek "rtl8723bs_bt" and "rtl8723ds_bt" userspace Bluetooth UART initialization tools (rtk_hciattach) use the following sequence:
1) send H5 sync pattern (already supported by hci_h5)
2) get LMP version (already supported by btrtl)
3) get ROM version (already supported by btrtl)
4) load the firmware and config for the current chipset (already supported by btrtl)
5) read UART settings from the config blob (already supported by btrtl)
6) send UART settings via a vendor command to the device (which changes the baudrate of the device and enables or disables flow control depending on the config)
7) change the baudrate and flow control settings on the host
8) send the firmware and config blob to the device (already supported by btrtl)

This uses the serdev library as well as the existing btrtl driver to initialize the Bluetooth functionality, which consists of:
- identifying the device and loading the corresponding firmware and config blobs (steps #2, #3 and #4)
- configuring the baudrate and flow control (steps #6 and #7)
- uploading the firmware to the device (step #8)

---
drivers/bluetooth/Kconfig | 1 +
drivers/bluetooth/hci_h5.c | 205 +++++++++++++++++++++++++++++++++++++++++++++

2 files changed, 206 insertions(+)
Quoted 14 lines
Quoted 5 lines
drivers/bluetooth/Kconfig +0 -0
index 60e1c7d6986d..3001f1200c72 100644
@@ -146,6 +146,7 @@ config BT_HCIUART_LL
Quoted 7 lines

config BT_HCIUART_3WIRE
bool "Three-wire UART (H5) protocol support" depends on BT_HCIUART
+ select BT_RTL if SERIAL_DEV_BUS
help
The HCI Three-wire UART Transport Layer makes it possible to user the Bluetooth HCI over a serial port interface. The HCI

while this is fine initially, I think long term this is not sustainable. So we need to abstract the H:5 part and the vendor specific setup part so that you can have a hci_rtl.c that uses H:5 instead of H:4 and still is as tiny and simple as our H:4 drivers.

I had a look at hci_ath to see how hci_h4 is re-usable and I think I get your point

do you have any specific solution already in mind or do you want to me make a suggestion (by providing another patch)?
if you don't have any specific in mind: do you have anything you want me to "consider" or "avoid"?

Quoted 1 line

One other think I am not really happy about here is the Kconfig entry itself. You need to know that you need 3WIRE and SERDEV to magically enable RTL UART support. Maybe it would be better to just have a CONFIG_HCIUART_RTL and duplicate the config entry just specific to RTL. It then can select RTL, but in the Makefile it will result in using the same hci_h5.c file.

if I understand you correctly this issue would go away once hci_h5 provides library functions (like hci_h4) since we would have a dedicated CONFIG_HCIUART_RTL anyways which could simply select BT_HCIUART_3WIRE

Quoted 194 lines
Quoted 194 lines
drivers/bluetooth/hci_h5.c +143 -0
index 6cfc2f276250..a03acc3b1b52 100644
@@ -28,7 +28,14 @@
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/serdev.h>
+
 #include "hci_uart.h"
+#include "btrtl.h"
 
 #define HCI_3WIRE_ACK_PKT     0
 #define HCI_3WIRE_LINK_PKT    15
@@ -97,6 +104,13 @@ struct h5 {
      } sleep;
 };
 
+struct h5_device {
+     struct hci_uart hu;
+     struct gpio_desc *enable_gpio;
+     struct gpio_desc *reset_gpio;
+     int (*vendor_setup)(struct h5_device *h5_dev);
+};
+
 static void h5_reset_rx(struct h5 *h5);
 
 static void h5_link_control(struct hci_uart *hu, const void *data, size_t len)
@@ -190,6 +204,7 @@ static int h5_open(struct hci_uart *hu)
 {
      struct h5 *h5;
      const unsigned char sync[] = { 0x01, 0x7e };
+     int err;
 
      BT_DBG("hu %p", hu);
 
@@ -210,6 +225,14 @@ static int h5_open(struct hci_uart *hu)
 
      h5->tx_win = H5_TX_WIN_MAX;
 
+     if (hu->serdev) {
+             err = serdev_device_open(hu->serdev);
+             if (err) {
+                     bt_dev_err(hu->hdev, "failed to open serdev: %d", err);
+                     return err;
+             }
+     }
+
      /* Send initial sync request */
      h5_link_control(hu, sync, sizeof(sync));
      mod_timer(&h5->timer, jiffies + H5_SYNC_TIMEOUT);
@@ -217,6 +240,25 @@ static int h5_open(struct hci_uart *hu)
      return 0;
 }
 
+static int h5_setup(struct hci_uart *hu)
+{
+     int err;
+     struct h5_device *h5_dev;
+
+     if (!hu->serdev)
+             return 0;
+
+     h5_dev = serdev_device_get_drvdata(hu->serdev);
+
+     if (h5_dev->vendor_setup) {
+             err = h5_dev->vendor_setup(h5_dev);
+             if (err)
+                     return err;
+     }
+
+     return 0;
+}
+
 static int h5_close(struct hci_uart *hu)
 {
      struct h5 *h5 = hu->priv;
@@ -227,6 +269,15 @@ static int h5_close(struct hci_uart *hu)
      skb_queue_purge(&h5->rel);
      skb_queue_purge(&h5->unrel);
 
+     if (hu->serdev) {
+             struct h5_device *h5_dev;
+
+             h5_dev = serdev_device_get_drvdata(hu->serdev);
+             gpiod_set_value_cansleep(h5_dev->enable_gpio, 0);
+
+             serdev_device_close(hu->serdev);
+     }
+
      kfree(h5);
 
      return 0;
@@ -736,10 +787,160 @@ static int h5_flush(struct hci_uart *hu)
      return 0;
 }
 
+#if IS_ENABLED(CONFIG_SERIAL_DEV_BUS)
+static int h5_setup_realtek(struct h5_device *h5_dev)
+{
+     struct hci_uart *hu = &h5_dev->hu;
+     int err = 0, retry = 3;
+     struct sk_buff *skb;
+     struct btrtl_device_info *btrtl_dev;
+     __le32 baudrate_data;
+     u32 device_baudrate;
+     unsigned int controller_baudrate;
+     bool flow_control;
+
+     /* devices always start with flow control disabled and even parity */
+     serdev_device_set_flow_control(hu->serdev, false);
+     serdev_device_set_parity(hu->serdev, true, false);
+
+     do {
+             /* disable the device and put it into reset. some devices only
+              * have one of these lines, so we toggle both here to support
+              * all combinations.
+              */
+             gpiod_set_value_cansleep(h5_dev->reset_gpio, 1);
+             gpiod_set_value_cansleep(h5_dev->enable_gpio, 0);
+
+             /* wait until the device is disabled and/or reset. 500ms are
+              * chosen by manually testing on a RTL8723BS. shorter wait
+              * times lead to a non-responding device.
+              */
+             msleep(500);
+
+             /* take the device out of reset and enable it. */
+             gpiod_set_value_cansleep(h5_dev->reset_gpio, 0);
+             gpiod_set_value_cansleep(h5_dev->enable_gpio, 1);
+
+             /* after that we need to wait 500ms, otherwise the device might
+              * not respond in all cases. this was determined by testing
+              * with a RTL8723BS.
+              */
+             msleep(500);
+
+             btrtl_dev = btrtl_initialize(hu->hdev);
+             if (!IS_ERR(btrtl_dev))
+                     break;
+
+             /* Toggle the enable and reset pins above and try again */
+     } while (retry--);
+
+     if (IS_ERR(btrtl_dev))
+             return PTR_ERR(btrtl_dev);
+
+     err = btrtl_get_uart_settings(hu->hdev, btrtl_dev,
+                                   &controller_baudrate, &device_baudrate,
+                                   &flow_control);
+     if (err)
+             goto out_free;
+
+     baudrate_data = cpu_to_le32(device_baudrate);
+     skb = __hci_cmd_sync(hu->hdev, 0xfc17, sizeof(baudrate_data),
+                          &baudrate_data, HCI_INIT_TIMEOUT);
+     if (IS_ERR(skb)) {
+             bt_dev_err(hu->hdev, "set baud rate command failed");
+             err = -PTR_ERR(skb);
+             goto out_free;
+     } else {
+             kfree_skb(skb);
+     }
+
+     serdev_device_set_baudrate(hu->serdev, controller_baudrate);
+     serdev_device_set_flow_control(hu->serdev, flow_control);
+
+     err = btrtl_download_firmware(hu->hdev, btrtl_dev);
+
+out_free:
+     btrtl_free(btrtl_dev);
+
+     return err;
+}
+
+static const struct hci_uart_proto h5p;
+
+static int hci_h5_probe(struct serdev_device *serdev)
+{
+     struct hci_uart *hu;
+     struct h5_device *h5_dev;
+
+     h5_dev = devm_kzalloc(&serdev->dev, sizeof(*h5_dev), GFP_KERNEL);
+     if (!h5_dev)
+             return -ENOMEM;
+
+     hu = &h5_dev->hu;
+     hu->serdev = serdev;
+
Quoted 8 lines

drivers/bluetooth/hci_h5.c static int h5_flush(struct hci_uart *hu) · line 887

Quoted 6 lines
drivers/bluetooth/hci_h5.c +6 -0
+     serdev_device_set_drvdata(serdev, h5_dev);
+
+     h5_dev->vendor_setup = of_device_get_match_data(&serdev->dev);
+
+     h5_dev->enable_gpio = devm_gpiod_get_optional(&serdev->dev, "enable",
+                                                    GPIOD_OUT_HIGH);

Indentation mistake.

I'll take care of that in the next version - thanks for spotting!

Quoted 68 lines
Quoted 68 lines
drivers/bluetooth/hci_h5.c +54 -0
+     if (IS_ERR(h5_dev->enable_gpio))
+             return PTR_ERR(h5_dev->enable_gpio);
+
+     h5_dev->reset_gpio = devm_gpiod_get_optional(&serdev->dev, "reset",
+                                                  GPIOD_OUT_LOW);
+     if (IS_ERR(h5_dev->reset_gpio))
+             return PTR_ERR(h5_dev->reset_gpio);
+
+     hci_uart_set_speeds(hu, 115200, 0);
+
+     return hci_uart_register_device(hu, &h5p);
+}
+
+static void hci_h5_remove(struct serdev_device *serdev)
+{
+     struct h5_device *h5_dev = serdev_device_get_drvdata(serdev);
+     struct hci_uart *hu = &h5_dev->hu;
+     struct hci_dev *hdev = hu->hdev;
+
+     cancel_work_sync(&hu->write_work);
+
+     hci_unregister_dev(hdev);
+     hci_free_dev(hdev);
+     hu->proto->close(hu);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id hci_h5_of_match[] = {
+     {
+             .compatible = "realtek,rtl8723bs-bluetooth",
+             .data = h5_setup_realtek
+     },
+     {
+             .compatible = "realtek,rtl8723ds-bluetooth",
+             .data = h5_setup_realtek
+     },
+     {},
+};
+MODULE_DEVICE_TABLE(of, hci_h5_of_match);
+#endif
+
+static struct serdev_device_driver hci_h5_drv = {
+     .driver         = {
+             .name   = "hci-h5",
+             .of_match_table = of_match_ptr(hci_h5_of_match),
+     },
+     .probe  = hci_h5_probe,
+     .remove = hci_h5_remove,
+};
+#endif
+
 static const struct hci_uart_proto h5p = {
      .id             = HCI_UART_3WIRE,
      .name           = "Three-wire (H5)",
      .open           = h5_open,
+     .setup          = h5_setup,
      .close          = h5_close,
      .recv           = h5_recv,
      .enqueue        = h5_enqueue,
@@ -749,10 +950,14 @@ static const struct hci_uart_proto h5p = {
 
 int __init h5_init(void)
 {
+     serdev_device_driver_register(&hci_h5_drv);
+
      return hci_uart_register_proto(&h5p);
 }
 
Quoted 6 lines

drivers/bluetooth/hci_h5.c static const struct hci_uart_proto h5p = { · line 963

Quoted 6 lines
drivers/bluetooth/hci_h5.c +2 -0
 int __exit h5_deinit(void)
 {
+     serdev_device_driver_unregister(&hci_h5_drv);
+
      return hci_uart_unregister_proto(&h5p);
 }

Regards
Martin