For some reason there is not much documentation on Linux Bluetooth stack. Especially if the most low level layer in question. There is one introduction by Albert Huang that covers very basic use-cases. It can be useful but does not go deep enough. At least for me.
Among other sources of information that are worthreading is BlueZ code. Tools and libraries written in C and Python. The code is not self-documented unfortunately.
This overview is intended to get some clue on what is going on with Bluetooth in Linux, and how user-space BlueZ is useful. Not accurate nor complete, I am newbie.
In kernel space BlueZ is a Bluetooth protocol stack. Can be found under /net/bluetooth/ in Linux source. It cannot be found under /Documentation/, kernel part of BlueZ is not documented.
In user-space in Debian it is:
On a filesystem, besides binaries, it is:
User-space part of BlueZ is somehow described in manpages and /usr/share/doc/bluez. DBus API documented under /doc/ in BlueZ sources.
The reference specification is written by Bluetooth special interest group: https://www.bluetooth.org/en-us/specification .
Most things in BlueZ implemented with network interface. So that communication is reading/writting sockets. Other things can be done with ioctl() interface.
If nothing controlled in user-space, controller still has some sane activity in ether. In particular, this should be enough to make controller pingable with «l2ping» (providing «hci0» interface exists):
# rfkill unblock bluetooth # hciconfig hci0 up # hciconfig hci0 pscan
In order to make device participate in more complex processes like pairing, an application should monitor HCI events via a raw socket and send appropriate HCI commands (with socket interface as well). As an example it can be a bluez-simple-agent (do not look at its source code, it does not work with HCI raw socket directly, but through DBus and bluetoothd).
Bluetooth services should be advertised with SDP service. A service itself and an SDP service that keeps a record about that one, are in general different processes. It is technically possible to advertise fake services with SDP service alone, as well as it is possible to run services without SDP. SDP is a network service that talks SDP protocol on a well-known L2CAP channel.
Bluetooth services are network services that talk the advertised protocol.
In all examples two Bluetooth adapter addresses will be used: «RE:MO:TE:AD:DR:RS» and «LO:CA:LE:AD:DR:RS». Bluetooth interface «hci0» assumed.
To avoid unnecessary notes about specific configuration flavors, there is one:
# rfkill unblock bluetooth # hciconfig hci0 up # hciconfig hci0 piscan
Tool for local traffic sniffing:
# hcidump -ti hci0
All presented tools can be interrupted with SIGINT or anything (Ctrl-C).
No any bluetoothd daemons supposed on slave machine. Everything done from scratch (in user-space).
On a slave:
$ make sdp-server ; sudo ./sdp-server cc sdp-server.c -Wall -Wextra -g -O2 -lbluetooth -o sdp-server sdp-server: Waiting for incoming connections... sdp-server: [4] acccepted incoming connection. sdp-server: [4] Session established with: RE:MO:TE:AD:DR:RS. sdp-server: [4] SDP PDU header for incoming connection: sdp-server: [4] PDU ID = 0x06 - SDP_ServiceSearchAttributeRequest sdp-server: [4] Transaction ID = 0x0000 sdp-server: [4] Parameters Length = 0x000F sdp-server: [4] process_sdp_pdu() finished. sdp-server: [4] process_session() finished. sdp-server: [4] connection closed. sdp-server: Waiting for incoming connections...
Meantime on a master:
$ sdptool browse LO:CA:LE:AD:DR:RS Browsing D0:E7:82:B5:09:D8 ... Service RecHandle: 0x10000 Service Class ID List: "Serial Port" (0x1101) Protocol Descriptor List: "RFCOMM" (0x0003) Channel: 11 Profile Descriptor List: "Serial Port" (0x1101) Version: 0x0100
On slave the first concurrent process:
$ make rfcomm-server ; sudo ./rfcomm-server cc rfcomm-server.c -Wall -Wextra -g -O2 -lbluetooth -o rfcomm-server rfcomm-server: Listening on RFCOMM channel 11. rfcomm-server: Incoming connection accepted. rfcomm-server: [4] sending MOTD: Incoming address: RE:MO:TE:AD:DR:RS, chan=11. Type anything to disconnect. rfcomm-server: [4] some input received. rfcomm-server: Listening on RFCOMM channel 11.
On slave the second concurrent process:
$ make hci-host ; sudo ./hci-host cc hci-host.c -Wall -Wextra -g -O2 -lbluetooth -o hci-host hci-host: first_dev() found device: 0. hci-host: recv(hdr) accepted: type=0x04, code=0x04, length=10. hci-host: dispatch_event() pass: 0x04. hci-host: recv(hdr) accepted: type=0x04, code=0x0F, length=4. hci-host: dispatch_event() pass: 0x0F. […] hci-host: recv(hdr) accepted: type=0x04, code=0x31, length=6. hci-host: dispatch_event() process: EVT_IO_CAPABILITY_REQUEST [0x31] hci-host: send_capabilities(): written successfully. hci-host: recv(hdr) accepted: type=0x04, code=0x0E, length=10. hci-host: dispatch_event() pass: 0x0E. hci-host: recv(hdr) accepted: type=0x04, code=0x33, length=10. hci-host: dispatch_event() process: EVT_USER_CONFIRM_REQUEST [0x33] hci-host: recv(hdr) accepted: type=0x04, code=0x0E, length=10. […] hci-host: recv(hdr) accepted: type=0x04, code=0x05, length=4. hci-host: dispatch_event() pass: 0x05.
On master, if it is Linux desktop, after those both processes are started on slave:
$ sudo rfcomm connect hci0 LO:CA:LE:AD:DR:RS 11 Connected /dev/rfcomm0 to LO:CA:LE:AD:DR:RS on channel 11 Press CTRL-C for hangup Disconnected
MOTD could be read from /dev/rfcomm0 if anybody cared. But also connecting with Terminal from Android works fine and displays the MOTD.
Note that master might have link key persistent across subsequent re-connections that is not implemented on the slave side. «service bluetooth restart» should be enough.