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
--
2.15.1
Hi Martin,
>>>>>>> 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?
>>>>
>>>>> 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
>
>>> 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)!
>
>>> 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.
>> 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.
> 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.
>> 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.
>> 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
>
>> 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
Hi Marcel,
thank you for digging into this!
On Fri, Jan 5, 2018 at 9:44 PM, Marcel Holtmann <[email protected]> wrote=
:
> Hi Carlo,
>
>>>>>> As Marcel suggested we can assume that the information in the DSDT i=
s
>>>>>> 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 m=
y
>>>>> 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 o=
ut what we actually have in these config files?
>>>
>>>> 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 sys=
tem and just allow specifying the UART settings via normal DT properties li=
ke 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=3D1 offset=3Df4,{ 01 }
>> len=3D2 offset=3Df6,{ 81 00 }
>> len=3D2 offset=3Dfa,{ 12 80 }
>> len=3D16 offset=3D0c,{ 04 50 00 00 50 c5 ea 19 e1 1b fd af 5b 01 a4 0b =
}
>> len=3D1 offset=3Dd9,{ 0f }
>> len=3D1 offset=3De4,{ 08 }
>> Analyzing rtl8723d_config.dms
>> Signature: 0x8723ab55
>> Data length: 41
>> len=3D1 offset=3Df4,{ 01 }
>> len=3D2 offset=3Df6,{ 81 00 }
>> len=3D2 offset=3Dfa,{ 12 80 }
>> len=3D16 offset=3D0c,{ 02 80 92 04 50 c5 ea 19 e1 1b fd af 5f 01 a4 0b =
}
>> len=3D1 offset=3Dd9,{ 0f }
>> len=3D1 offset=3De4,{ 08 }
>> Analyzing rtl8822b_config.bin
>> Signature: 0x8723ab55
>> Data length: 8
>> len=3D1 offset=3Dd9,{ 0f }
>> len=3D1 offset=3De4,{ 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 c=
onfiguration. It would be good to know which settings the other ones contro=
l.
this matches my findings from the hciattach_rtk tool for rtl8723bs_bt
and rtl8723ds_bt
>> 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)!
>> Actually hciattach_rtk just takes the baud rate and and hardware flow co=
ntrol bit out of this file. That is clearly two things that are better writ=
ten 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 is actually some funny stuff if you start to understand it.
>
> Analyzing rtl8723b_config.dms
> Signature: 0x8723ab55
> Data len: 38
> len=3D8 offset=3D00f4,{ 01 00 00 00 05 50 00 00 }
> len=3D16 offset=3D000c,{ 02 80 92 04 50 c5 ea 19 e1 1b f1 af 5f 01 a4 0b=
},UART_CONFIG
> len=3D1 offset=3D0027,{ 63 }
> len=3D1 offset=3D00fe,{ 01 }
> Analyzing rtl8723d_config_1000000_noflow.dms
> Signature: 0x8723ab55
> Data len: 41
> len=3D1 offset=3D00f4,{ 01 }
> len=3D2 offset=3D00f6,{ 81 00 }
> len=3D2 offset=3D00fa,{ 12 80 }
> len=3D16 offset=3D000c,{ 04 50 00 00 50 c5 ea 19 e1 1b fd af 5b 01 a4 0b=
},UART_CONFIG
> len=3D1 offset=3D00d9,{ 0f }
> len=3D1 offset=3D00e4,{ 08 }
>
> Seems like Realtek really defines memory offsets in this file and they ca=
n 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)
> So 00f4,{ 01 00 00 00 05 50 00 00 } defines the whole PCM settings for in=
terface 1 and 2. And 00f4,{ 01 } + 00f6,{ 81 00 } + 00fa,{ 12 80 } is the s=
ame 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 han=
dles the flow control bit. It works if the config files all have offset 000=
c in it, but if not, then they are going funky.
>
> Since these are efuse settings, I really wonder if there is just a HCI ve=
ndor 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. S=
ince 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]
> Anyway, I am certain that for Realtek UART devices, we just want to speci=
fy 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 p=
rovided 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
> 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...)
> At the end of the day, this is the same as for Broadcom and ACPI vs DT. T=
his 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 t=
iny 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/h=
ciattach_rtk.c#L2742
[1] https://elixir.free-electrons.com/linux/v4.14.11/source/drivers/bluetoo=
th/btrtl.c#L377
Hi Carlo,
>>>>> 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?
>>
>>> 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.
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
Hi Carlo,
>>>> 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?
>
>> 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
Hi Carlo,
>>> 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?
> 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.
>> 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.
>
>> 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
>
> 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
On Wed, Jan 3, 2018 at 8:50 PM, Martin Blumenstingl
<[email protected]> wrote:
> Hi Carlo,
Hi Martin,
>> 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.
> 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.
> 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
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 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)
>
>> 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
>> I'll test your patches on the hardware I have.
> great, thank you!
Cheers!
--
Carlo Caione | +44.7384.69.16.04 | Endless
Hi Carlo,
On Wed, Jan 3, 2018 at 12:06 AM, Carlo Caione <[email protected]> wrote:
> On Tue, Jan 2, 2018 at 9:46 PM, Martin Blumenstingl
> <[email protected]> wrote:
>> Hi Carlo,
>
> Hi Martin,
>
>> On Tue, Jan 2, 2018 at 12:31 PM, Carlo Caione <[email protected]> wrote=
:
>>> On Tue, Jan 2, 2018 at 11:19 AM, Marcel Holtmann <[email protected]> =
wrote:
>>>> Hi Carlo,
>>>>
>>>>>>> Some Realtek bluetooth devices need a "config" blob. The btrtl driv=
er
>>>>>>> currently only allows loading this config blob via the request_firm=
ware
>>>>>>> mechanism.
>>>>>>>
>>>>>>> The UART Bluetooth chips use this config blob to specify the baudra=
te,
>>>>>>> whether flow control is used and some other unknown bits. This mean=
s
>>>>>>> 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 futu=
re
>>>>>>> this could be extended to support ACPI as well (in case that's need=
ed).
>>>>>>>
>>>>>>> Parse the devicetree node if it exists and obtain the config blob f=
rom
>>>>>>> there. Otherwise fall back to using the "old" request_firmware
>>>>>>> mechanism.
>>>>>>
>>>>>> where are these config blobs coming from? I think we also need to gi=
ve people a helping hand on how to add them to DT. I still wonder if the on=
ly 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 s=
ee 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/82bff95ababb67dd33f52a86e94ce3f=
f
>> 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).
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 (=3D 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)
> 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?
> I'll test your patches on the hardware I have.
great, thank you!
Regards
Martin
Hi Rob,
On Wed, Jan 3, 2018 at 7:38 PM, Rob Herring <[email protected]> wrote:
> On Mon, Jan 1, 2018 at 2:42 PM, Martin Blumenstingl
> <[email protected]> wrote:
>> 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)?
>> 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.
thank you for having a look at this patch!
I will update the description in the next version
>> 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.
>>
>> Signed-off-by: Martin Blumenstingl <[email protected]>
>> ---
>> 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
Hi Loic,
On Wed, Jan 3, 2018 at 6:14 PM, Loic Poulain <[email protected]> wrote:
> Hi Martin,
>
> On 2 January 2018 at 22:06, Martin Blumenstingl
> <[email protected]> wrote:
>> Hi Marcel,
>>
>> thank you for looking into this latest version!
>>
>> On Tue, Jan 2, 2018 at 12:04 PM, Marcel Holtmann <[email protected]> wrote:
>>> Hi Martin,
>>>
>>>> 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.
>>>>
>>>> Signed-off-by: Martin Blumenstingl <[email protected]>
>
> 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
On Tue, Jan 2, 2018 at 3:10 PM, Martin Blumenstingl
<[email protected]> wrote:
> Hi Marcel,
>
> On Tue, Jan 2, 2018 at 12:16 PM, Marcel Holtmann <[email protected]> wrote:
>> Hi Martin,
>>
>>> 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.
>>>
>>> Signed-off-by: Martin Blumenstingl <[email protected]>
>>> ---
>>> .../devicetree/bindings/net/realtek-bluetooth.txt | 41 ++++++++++++++++++++++
>>> 1 file changed, 41 insertions(+)
>>> create mode 100644 Documentation/devicetree/bindings/net/realtek-bluetooth.txt
>>>
>>> diff --git a/Documentation/devicetree/bindings/net/realtek-bluetooth.txt b/Documentation/devicetree/bindings/net/realtek-bluetooth.txt
>>> new file mode 100644
>>> index 000000000000..1491329c4cd1
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/net/realtek-bluetooth.txt
>>> @@ -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.
>>
>> 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)?
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.
> 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
>
>>> +- 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
On Mon, Jan 1, 2018 at 2:42 PM, Martin Blumenstingl
<[email protected]> wrote:
> 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.
> 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.
> 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.
>
> Signed-off-by: Martin Blumenstingl <[email protected]>
> ---
> 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(-)
Hi Martin,
On 2 January 2018 at 22:06, Martin Blumenstingl
<[email protected]> wrote:
> Hi Marcel,
>
> thank you for looking into this latest version!
>
> On Tue, Jan 2, 2018 at 12:04 PM, Marcel Holtmann <[email protected]> wrote:
>> Hi Martin,
>>
>>> 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.
>>>
>>> Signed-off-by: Martin Blumenstingl <[email protected]>
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
Hi Martin,
>>>> 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.
>>>>
>>>> Signed-off-by: Martin Blumenstingl <[email protected]>
>>>> ---
>>>> drivers/tty/serdev/core.c | 12 ++++++++++++
>>>> drivers/tty/serdev/serdev-ttyport.c | 21 +++++++++++++++++++++
>>>> include/linux/serdev.h | 5 +++++
>>>> 3 files changed, 38 insertions(+)
>>>>
>>>> diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c
>>>> index 1bef39828ca7..d327b02980f5 100644
>>>> --- a/drivers/tty/serdev/core.c
>>>> +++ b/drivers/tty/serdev/core.c
>>>> @@ -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);
>>>> +
>>>
>>> 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)
> 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
On Tue, Jan 02, 2018 at 10:34:04PM +0100, Martin Blumenstingl wrote:
> On Tue, Jan 2, 2018 at 10:16 PM, Martin Blumenstingl
> <[email protected]> wrote:
> > Hi Marcel, Hi Rob,
> >
> > On Tue, Jan 2, 2018 at 12:16 PM, Marcel Holtmann <[email protected]> wrote:
> >> Hi Martin,
> >>
> >>> 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.
> >>>
> >>> Signed-off-by: Martin Blumenstingl <[email protected]>
> >>> ---
> >>> drivers/tty/serdev/core.c | 12 ++++++++++++
> >>> drivers/tty/serdev/serdev-ttyport.c | 21 +++++++++++++++++++++
> >>> include/linux/serdev.h | 5 +++++
> >>> 3 files changed, 38 insertions(+)
> >>>
> >>> diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c
> >>> index 1bef39828ca7..d327b02980f5 100644
> >>> --- a/drivers/tty/serdev/core.c
> >>> +++ b/drivers/tty/serdev/core.c
> >>> @@ -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);
> >>> +
> >>
> >> 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)
> 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
On Tue, Jan 2, 2018 at 9:46 PM, Martin Blumenstingl
<[email protected]> wrote:
> Hi Carlo,
Hi Martin,
> On Tue, Jan 2, 2018 at 12:31 PM, Carlo Caione <[email protected]> wrote:
>> On Tue, Jan 2, 2018 at 11:19 AM, Marcel Holtmann <[email protected]> w=
rote:
>>> Hi Carlo,
>>>
>>>>>> Some Realtek bluetooth devices need a "config" blob. The btrtl drive=
r
>>>>>> currently only allows loading this config blob via the request_firmw=
are
>>>>>> mechanism.
>>>>>>
>>>>>> The UART Bluetooth chips use this config blob to specify the baudrat=
e,
>>>>>> 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 futur=
e
>>>>>> this could be extended to support ACPI as well (in case that's neede=
d).
>>>>>>
>>>>>> Parse the devicetree node if it exists and obtain the config blob fr=
om
>>>>>> there. Otherwise fall back to using the "old" request_firmware
>>>>>> mechanism.
>>>>>
>>>>> where are these config blobs coming from? I think we also need to giv=
e people a helping hand on how to add them to DT. I still wonder if the onl=
y pieces we are using are the UART config, then maybe skipping the config b=
lob 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 se=
e 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?
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,
--=20
Carlo Caione | +44.7384.69.16.04 | Endless
Hi Carlo,
On Tue, Jan 2, 2018 at 12:31 PM, Carlo Caione <[email protected]> wrote:
> On Tue, Jan 2, 2018 at 11:19 AM, Marcel Holtmann <[email protected]> wr=
ote:
>> Hi Carlo,
>>
>>>>> Some Realtek bluetooth devices need a "config" blob. The btrtl driver
>>>>> currently only allows loading this config blob via the request_firmwa=
re
>>>>> 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 fro=
m
>>>>> 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 bl=
ob 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
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
Hi Marcel, Hi Carlo,
On Tue, Jan 2, 2018 at 12:38 PM, Marcel Holtmann <[email protected]> wrot=
e:
> Hi Carlo,
>
>>>>>> Some Realtek bluetooth devices need a "config" blob. The btrtl drive=
r
>>>>>> currently only allows loading this config blob via the request_firmw=
are
>>>>>> mechanism.
>>>>>>
>>>>>> The UART Bluetooth chips use this config blob to specify the baudrat=
e,
>>>>>> 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 futur=
e
>>>>>> this could be extended to support ACPI as well (in case that's neede=
d).
>>>>>>
>>>>>> Parse the devicetree node if it exists and obtain the config blob fr=
om
>>>>>> there. Otherwise fall back to using the "old" request_firmware
>>>>>> mechanism.
>>>>>
>>>>> where are these config blobs coming from? I think we also need to giv=
e people a helping hand on how to add them to DT. I still wonder if the onl=
y pieces we are using are the UART config, then maybe skipping the config b=
lob 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 se=
e 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. I=
t 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 he=
re.
what I can see is (which also matches the settings I use on my Amlogic
based boards):
- initial speed =3D 115200
- 8 data bits
- 1 stop bit
- even parity
- HW flow control enabled
> I think that we extended serdev to ACPI as well. Don=E2=80=99t recall of =
the top of my head if these patches were merged or not. But if they are the=
n it is as simple as a serdev DT based driver. Just add the appropriate _HI=
D 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
> 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 c=
hips seems a bit cleaner direction. Frankly only H:4 and H:5 plain protocol=
s 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
On Tue, Jan 2, 2018 at 10:16 PM, Martin Blumenstingl
<[email protected]> wrote:
> Hi Marcel, Hi Rob,
>
> On Tue, Jan 2, 2018 at 12:16 PM, Marcel Holtmann <[email protected]> wr=
ote:
>> Hi Martin,
>>
>>> 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.
>>>
>>> Signed-off-by: Martin Blumenstingl <[email protected]>
>>> ---
>>> drivers/tty/serdev/core.c | 12 ++++++++++++
>>> drivers/tty/serdev/serdev-ttyport.c | 21 +++++++++++++++++++++
>>> include/linux/serdev.h | 5 +++++
>>> 3 files changed, 38 insertions(+)
>>>
>>> diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c
>>> index 1bef39828ca7..d327b02980f5 100644
>>> --- a/drivers/tty/serdev/core.c
>>> +++ b/drivers/tty/serdev/core.c
>>> @@ -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 enabl=
e,
>>> + bool odd)
>>> +{
>>> + struct serdev_controller *ctrl =3D 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=E2=80=99s 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)
I just discovered: such a patch was already posted by Ulrich Hecht: [0]
[0] https://patchwork.kernel.org/patch/9903787/
Hi Marcel,
On Tue, Jan 2, 2018 at 12:11 PM, Marcel Holtmann <[email protected]> wrot=
e:
> Hi Martin,
>
>> 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)
>>
>> Signed-off-by: Martin Blumenstingl <[email protected]>
>> ---
>> drivers/bluetooth/Kconfig | 1 +
>> drivers/bluetooth/hci_h5.c | 205 +++++++++++++++++++++++++++++++++++++++=
++++++
>> 2 files changed, 206 insertions(+)
>>
>> diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
>> index 60e1c7d6986d..3001f1200c72 100644
>> --- a/drivers/bluetooth/Kconfig
>> +++ b/drivers/bluetooth/Kconfig
>> @@ -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
>
> 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 t=
hat you can have a hci_rtl.c that uses H:5 instead of H:4 and still is as t=
iny 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"?
> One other think I am not really happy about here is the Kconfig entry its=
elf. You need to know that you need 3WIRE and SERDEV to magically enable RT=
L 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
>> diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c
>> index 6cfc2f276250..a03acc3b1b52 100644
>> --- a/drivers/bluetooth/hci_h5.c
>> +++ b/drivers/bluetooth/hci_h5.c
>> @@ -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[] =3D { 0x01, 0x7e };
>> + int err;
>>
>> BT_DBG("hu %p", hu);
>>
>> @@ -210,6 +225,14 @@ static int h5_open(struct hci_uart *hu)
>>
>> h5->tx_win =3D H5_TX_WIN_MAX;
>>
>> + if (hu->serdev) {
>> + err =3D 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 =3D serdev_device_get_drvdata(hu->serdev);
>> +
>> + if (h5_dev->vendor_setup) {
>> + err =3D h5_dev->vendor_setup(h5_dev);
>> + if (err)
>> + return err;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> static int h5_close(struct hci_uart *hu)
>> {
>> struct h5 *h5 =3D 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 =3D 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 =3D &h5_dev->hu;
>> + int err =3D 0, retry =3D 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 supp=
ort
>> + * 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 a=
re
>> + * 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 testin=
g
>> + * with a RTL8723BS.
>> + */
>> + msleep(500);
>> +
>> + btrtl_dev =3D 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 =3D btrtl_get_uart_settings(hu->hdev, btrtl_dev,
>> + &controller_baudrate, &device_baudra=
te,
>> + &flow_control);
>> + if (err)
>> + goto out_free;
>> +
>> + baudrate_data =3D cpu_to_le32(device_baudrate);
>> + skb =3D __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 =3D -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 =3D 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 =3D devm_kzalloc(&serdev->dev, sizeof(*h5_dev), GFP_KERNEL)=
;
>> + if (!h5_dev)
>> + return -ENOMEM;
>> +
>> + hu =3D &h5_dev->hu;
>> + hu->serdev =3D serdev;
>> +
>> + serdev_device_set_drvdata(serdev, h5_dev);
>> +
>> + h5_dev->vendor_setup =3D of_device_get_match_data(&serdev->dev);
>> +
>> + h5_dev->enable_gpio =3D devm_gpiod_get_optional(&serdev->dev, "ena=
ble",
>> + GPIOD_OUT_HIGH);
>
> Indentation mistake.
I'll take care of that in the next version - thanks for spotting!
>> + if (IS_ERR(h5_dev->enable_gpio))
>> + return PTR_ERR(h5_dev->enable_gpio);
>> +
>> + h5_dev->reset_gpio =3D devm_gpiod_get_optional(&serdev->dev, "rese=
t",
>> + 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 =3D serdev_device_get_drvdata(serdev);
>> + struct hci_uart *hu =3D &h5_dev->hu;
>> + struct hci_dev *hdev =3D 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[] =3D {
>> + {
>> + .compatible =3D "realtek,rtl8723bs-bluetooth",
>> + .data =3D h5_setup_realtek
>> + },
>> + {
>> + .compatible =3D "realtek,rtl8723ds-bluetooth",
>> + .data =3D h5_setup_realtek
>> + },
>> + {},
>> +};
>> +MODULE_DEVICE_TABLE(of, hci_h5_of_match);
>> +#endif
>> +
>> +static struct serdev_device_driver hci_h5_drv =3D {
>> + .driver =3D {
>> + .name =3D "hci-h5",
>> + .of_match_table =3D of_match_ptr(hci_h5_of_match),
>> + },
>> + .probe =3D hci_h5_probe,
>> + .remove =3D hci_h5_remove,
>> +};
>> +#endif
>> +
>> static const struct hci_uart_proto h5p =3D {
>> .id =3D HCI_UART_3WIRE,
>> .name =3D "Three-wire (H5)",
>> .open =3D h5_open,
>> + .setup =3D h5_setup,
>> .close =3D h5_close,
>> .recv =3D h5_recv,
>> .enqueue =3D h5_enqueue,
>> @@ -749,10 +950,14 @@ static const struct hci_uart_proto h5p =3D {
>>
>> int __init h5_init(void)
>> {
>> + serdev_device_driver_register(&hci_h5_drv);
>> +
>> 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
Martin
Hi Marcel, Hi Rob,
On Tue, Jan 2, 2018 at 12:16 PM, Marcel Holtmann <[email protected]> wrot=
e:
> Hi Martin,
>
>> 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.
>>
>> Signed-off-by: Martin Blumenstingl <[email protected]>
>> ---
>> drivers/tty/serdev/core.c | 12 ++++++++++++
>> drivers/tty/serdev/serdev-ttyport.c | 21 +++++++++++++++++++++
>> include/linux/serdev.h | 5 +++++
>> 3 files changed, 38 insertions(+)
>>
>> diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c
>> index 1bef39828ca7..d327b02980f5 100644
>> --- a/drivers/tty/serdev/core.c
>> +++ b/drivers/tty/serdev/core.c
>> @@ -225,6 +225,18 @@ void serdev_device_set_flow_control(struct serdev_d=
evice *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 =3D 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=E2=80=99s 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
Hi Marcel,
On Tue, Jan 2, 2018 at 12:16 PM, Marcel Holtmann <[email protected]> wrote:
> Hi Martin,
>
>> 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.
>>
>> Signed-off-by: Martin Blumenstingl <[email protected]>
>> ---
>> .../devicetree/bindings/net/realtek-bluetooth.txt | 41 ++++++++++++++++++++++
>> 1 file changed, 41 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/net/realtek-bluetooth.txt
>>
>> diff --git a/Documentation/devicetree/bindings/net/realtek-bluetooth.txt b/Documentation/devicetree/bindings/net/realtek-bluetooth.txt
>> new file mode 100644
>> index 000000000000..1491329c4cd1
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/net/realtek-bluetooth.txt
>> @@ -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.
>
> 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
>> +- 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>;
>> + };
>> +};
Regards
Martin
Hi Marcel,
thank you for looking into this latest version!
On Tue, Jan 2, 2018 at 12:04 PM, Marcel Holtmann <[email protected]> wrote:
> Hi Martin,
>
>> 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.
>>
>> Signed-off-by: Martin Blumenstingl <[email protected]>
>
> 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)!
> 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)
>> ---
>> 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(-)
>>
>> diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c
>> index 6a8d0d06aba7..6cfc2f276250 100644
>> --- a/drivers/bluetooth/hci_h5.c
>> +++ b/drivers/bluetooth/hci_h5.c
>> @@ -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");
>> diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
>> index c823914b3a80..5dd3e1bebfe4 100644
>> --- a/drivers/bluetooth/hci_ldisc.c
>> +++ b/drivers/bluetooth/hci_ldisc.c
>> @@ -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);
>>
>> diff --git a/drivers/bluetooth/hci_serdev.c b/drivers/bluetooth/hci_serdev.c
>> index e0e6461b9200..fe67eb6d4278 100644
>> --- a/drivers/bluetooth/hci_serdev.c
>> +++ b/drivers/bluetooth/hci_serdev.c
>> @@ -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;
>> diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h
>> index 66e8c68e4607..47e755ff4092 100644
>> --- a/drivers/bluetooth/hci_uart.h
>> +++ b/drivers/bluetooth/hci_uart.h
>> @@ -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.
OK, I'll do it that way in the next version
>> #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,
Regards
Martin
Hi Carlo,
>>>>> 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.
>>>>
>>>> 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
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
On Tue, Jan 2, 2018 at 11:19 AM, Marcel Holtmann <[email protected]> wrot=
e:
> Hi Carlo,
>
>>>> Some Realtek bluetooth devices need a "config" blob. The btrtl driver
>>>> currently only allows loading this config blob via the request_firmwar=
e
>>>> 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.
>>>
>>> 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 blo=
b 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,
--=20
Carlo Caione | +44.7384.69.16.04 | Endless
Hi Carlo,
>>> 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.
>>
>> 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
Hi Martin,
> 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.
>
> Signed-off-by: Martin Blumenstingl <[email protected]>
> ---
> drivers/tty/serdev/core.c | 12 ++++++++++++
> drivers/tty/serdev/serdev-ttyport.c | 21 +++++++++++++++++++++
> include/linux/serdev.h | 5 +++++
> 3 files changed, 38 insertions(+)
>
> diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c
> index 1bef39828ca7..d327b02980f5 100644
> --- a/drivers/tty/serdev/core.c
> +++ b/drivers/tty/serdev/core.c
> @@ -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);
> +
this really needs Rob’s ACK before I take the patch.
Regards
Marcel
Hi Martin,
> 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.
>
> Signed-off-by: Martin Blumenstingl <[email protected]>
> ---
> .../devicetree/bindings/net/realtek-bluetooth.txt | 41 ++++++++++++++++++++++
> 1 file changed, 41 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/net/realtek-bluetooth.txt
>
> diff --git a/Documentation/devicetree/bindings/net/realtek-bluetooth.txt b/Documentation/devicetree/bindings/net/realtek-bluetooth.txt
> new file mode 100644
> index 000000000000..1491329c4cd1
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/net/realtek-bluetooth.txt
> @@ -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.
any chance we can at least include the basic format of these config blobs. And I prefer at least an ACK from Rob here.
> +- 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>;
> + };
> +};
Regards
Marcel
On Tue, Jan 2, 2018 at 11:11 AM, Marcel Holtmann <[email protected]> wrot=
e:
> Hi Martin,
>
>> 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.
>
> where are these config blobs coming from? I think we also need to give pe=
ople a helping hand on how to add them to DT. I still wonder if the only pi=
eces 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,
--=20
Carlo Caione | +44.7384.69.16.04 | Endless
Hi Martin,
> 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.
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
Hi Martin,
> 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)
>
> Signed-off-by: Martin Blumenstingl <[email protected]>
> ---
> drivers/bluetooth/Kconfig | 1 +
> drivers/bluetooth/hci_h5.c | 205 +++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 206 insertions(+)
>
> diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
> index 60e1c7d6986d..3001f1200c72 100644
> --- a/drivers/bluetooth/Kconfig
> +++ b/drivers/bluetooth/Kconfig
> @@ -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
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.
> diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c
> index 6cfc2f276250..a03acc3b1b52 100644
> --- a/drivers/bluetooth/hci_h5.c
> +++ b/drivers/bluetooth/hci_h5.c
> @@ -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;
> +
> + 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.
> + 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);
> }
>
> int __exit h5_deinit(void)
> {
> + serdev_device_driver_unregister(&hci_h5_drv);
> +
> return hci_uart_unregister_proto(&h5p);
> }
Regards
Marcel
Hi Martin,
> 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.
>
> Signed-off-by: Martin Blumenstingl <[email protected]>
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.
> ---
> 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(-)
>
> diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c
> index 6a8d0d06aba7..6cfc2f276250 100644
> --- a/drivers/bluetooth/hci_h5.c
> +++ b/drivers/bluetooth/hci_h5.c
> @@ -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");
> diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
> index c823914b3a80..5dd3e1bebfe4 100644
> --- a/drivers/bluetooth/hci_ldisc.c
> +++ b/drivers/bluetooth/hci_ldisc.c
> @@ -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);
>
> diff --git a/drivers/bluetooth/hci_serdev.c b/drivers/bluetooth/hci_serdev.c
> index e0e6461b9200..fe67eb6d4278 100644
> --- a/drivers/bluetooth/hci_serdev.c
> +++ b/drivers/bluetooth/hci_serdev.c
> @@ -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;
> diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h
> index 66e8c68e4607..47e755ff4092 100644
> --- a/drivers/bluetooth/hci_uart.h
> +++ b/drivers/bluetooth/hci_uart.h
> @@ -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.
> #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,
Regards
Marcel
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)
Signed-off-by: Martin Blumenstingl <[email protected]>
---
drivers/bluetooth/Kconfig | 1 +
drivers/bluetooth/hci_h5.c | 205 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 206 insertions(+)
diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
index 60e1c7d6986d..3001f1200c72 100644
--- a/drivers/bluetooth/Kconfig
+++ b/drivers/bluetooth/Kconfig
@@ -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
diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c
index 6cfc2f276250..a03acc3b1b52 100644
--- a/drivers/bluetooth/hci_h5.c
+++ b/drivers/bluetooth/hci_h5.c
@@ -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;
+
+ 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);
+ 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);
}
int __exit h5_deinit(void)
{
+ serdev_device_driver_unregister(&hci_h5_drv);
+
return hci_uart_unregister_proto(&h5p);
}
--
2.15.1
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.
Signed-off-by: Martin Blumenstingl <[email protected]>
---
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(-)
diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c
index 6a8d0d06aba7..6cfc2f276250 100644
--- a/drivers/bluetooth/hci_h5.c
+++ b/drivers/bluetooth/hci_h5.c
@@ -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");
diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
index c823914b3a80..5dd3e1bebfe4 100644
--- a/drivers/bluetooth/hci_ldisc.c
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -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);
diff --git a/drivers/bluetooth/hci_serdev.c b/drivers/bluetooth/hci_serdev.c
index e0e6461b9200..fe67eb6d4278 100644
--- a/drivers/bluetooth/hci_serdev.c
+++ b/drivers/bluetooth/hci_serdev.c
@@ -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;
diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h
index 66e8c68e4607..47e755ff4092 100644
--- a/drivers/bluetooth/hci_uart.h
+++ b/drivers/bluetooth/hci_uart.h
@@ -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,
--
2.15.1
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.
Signed-off-by: Martin Blumenstingl <[email protected]>
---
drivers/bluetooth/btrtl.c | 43 +++++++++++++++++++++++++++++++++++++++++--
1 file changed, 41 insertions(+), 2 deletions(-)
diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c
index 25e9743643f5..1494269cf4d7 100644
--- a/drivers/bluetooth/btrtl.c
+++ b/drivers/bluetooth/btrtl.c
@@ -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;
--
2.15.1
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).
Signed-off-by: Martin Blumenstingl <[email protected]>
---
drivers/bluetooth/btrtl.c | 32 ++++++++++++++++++++++++++++++--
1 file changed, 30 insertions(+), 2 deletions(-)
diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c
index b6e8da4b73f8..25e9743643f5 100644
--- a/drivers/bluetooth/btrtl.c
+++ b/drivers/bluetooth/btrtl.c
@@ -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");
--
2.15.1
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).
Signed-off-by: Martin Blumenstingl <[email protected]>
---
drivers/bluetooth/btrtl.c | 112 ++++++++++++++++++++++++++++++++++++++++++++++
drivers/bluetooth/btrtl.h | 25 +++++++++++
2 files changed, 137 insertions(+)
diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c
index 300ee15875e6..b6e8da4b73f8 100644
--- a/drivers/bluetooth/btrtl.c
+++ b/drivers/bluetooth/btrtl.c
@@ -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);
diff --git a/drivers/bluetooth/btrtl.h b/drivers/bluetooth/btrtl.h
index 21c28dcbe5e0..2e7856fa5ac7 100644
--- a/drivers/bluetooth/btrtl.h
+++ b/drivers/bluetooth/btrtl.h
@@ -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
--
2.15.1
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.
Signed-off-by: Martin Blumenstingl <[email protected]>
---
drivers/bluetooth/btrtl.c | 273 +++++++++++++++++++++++++++-------------------
drivers/bluetooth/btrtl.h | 21 ++++
2 files changed, 180 insertions(+), 114 deletions(-)
diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c
index 3e5296287808..300ee15875e6 100644
--- a/drivers/bluetooth/btrtl.c
+++ b/drivers/bluetooth/btrtl.c
@@ -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]>");
diff --git a/drivers/bluetooth/btrtl.h b/drivers/bluetooth/btrtl.h
index 38ffe4890cd1..21c28dcbe5e0 100644
--- a/drivers/bluetooth/btrtl.h
+++ b/drivers/bluetooth/btrtl.h
@@ -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;
--
2.15.1
This makes the firmware names show up in modinfo.
Signed-off-by: Martin Blumenstingl <[email protected]>
---
drivers/bluetooth/btrtl.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c
index 6e2ad748abba..3e5296287808 100644
--- a/drivers/bluetooth/btrtl.c
+++ b/drivers/bluetooth/btrtl.c
@@ -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");
--
2.15.1
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.
Signed-off-by: Martin Blumenstingl <[email protected]>
---
.../devicetree/bindings/net/realtek-bluetooth.txt | 41 ++++++++++++++++++++++
1 file changed, 41 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/realtek-bluetooth.txt
diff --git a/Documentation/devicetree/bindings/net/realtek-bluetooth.txt b/Documentation/devicetree/bindings/net/realtek-bluetooth.txt
new file mode 100644
index 000000000000..1491329c4cd1
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/realtek-bluetooth.txt
@@ -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>;
+ };
+};
--
2.15.1
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.
Signed-off-by: Martin Blumenstingl <[email protected]>
---
drivers/tty/serdev/core.c | 12 ++++++++++++
drivers/tty/serdev/serdev-ttyport.c | 21 +++++++++++++++++++++
include/linux/serdev.h | 5 +++++
3 files changed, 38 insertions(+)
diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c
index 1bef39828ca7..d327b02980f5 100644
--- a/drivers/tty/serdev/core.c
+++ b/drivers/tty/serdev/core.c
@@ -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;
diff --git a/drivers/tty/serdev/serdev-ttyport.c b/drivers/tty/serdev/serdev-ttyport.c
index 247788a16f0b..36bed86e5c96 100644
--- a/drivers/tty/serdev/serdev-ttyport.c
+++ b/drivers/tty/serdev/serdev-ttyport.c
@@ -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,
diff --git a/include/linux/serdev.h b/include/linux/serdev.h
index d609e6dc5bad..07f2d4d4e88c 100644
--- a/include/linux/serdev.h
+++ b/include/linux/serdev.h
@@ -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)
--
2.15.1