From: Luiz Augusto von Dentz <luiz.von.dentz@xxxxxxxxx> This adds initial documentation for ISO sockets. --- Makefile.am | 8 +- doc/iso.rst | 436 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 441 insertions(+), 3 deletions(-) create mode 100644 doc/iso.rst diff --git a/Makefile.am b/Makefile.am index c4b88d83c962..8319373409cd 100644 --- a/Makefile.am +++ b/Makefile.am @@ -356,7 +356,7 @@ CLEANFILES += $(builtin_files) if MANPAGES man_MANS += src/bluetoothd.8 -man_MANS += doc/hci.7 doc/mgmt.7 doc/l2cap.7 doc/rfcomm.7 doc/sco.7 +man_MANS += doc/hci.7 doc/mgmt.7 doc/l2cap.7 doc/rfcomm.7 doc/sco.7 doc/iso.7 man_MANS += doc/org.bluez.Adapter.5 doc/org.bluez.Device.5 \ doc/org.bluez.DeviceSet.5 doc/org.bluez.AgentManager.5 \ doc/org.bluez.Agent.5 doc/org.bluez.ProfileManager.5 \ @@ -390,7 +390,8 @@ man_MANS += doc/org.bluez.obex.Client.5 doc/org.bluez.obex.Session.5 \ doc/org.bluez.obex.Image.5 endif manual_pages += src/bluetoothd.8 -manual_pages += doc/hci.7 doc/mgmt.7 doc/l2cap.7 doc/rfcomm.7 doc/sco.7 +manual_pages += doc/hci.7 doc/mgmt.7 doc/l2cap.7 doc/rfcomm.7 doc/sco.7 \ + doc/iso.7 manual_pages += doc/org.bluez.Adapter.5 doc/org.bluez.Device.5 \ doc/org.bluez.DeviceSet.5 doc/org.bluez.AgentManager.5 \ doc/org.bluez.Agent.5 doc/org.bluez.ProfileManager.5 \ @@ -467,7 +468,8 @@ EXTRA_DIST += doc/assigned-numbers.txt doc/supported-features.txt \ EXTRA_DIST += doc/health-api.txt \ doc/sap-api.txt -EXTRA_DIST += doc/hci.rst doc/mgmt.rst doc/l2cap.rst doc/rfcomm.rst doc/sco.rst +EXTRA_DIST += doc/hci.rst doc/mgmt.rst doc/l2cap.rst doc/rfcomm.rst \ + doc/sco.rst doc/iso.rst EXTRA_DIST += doc/org.bluez.Adapter.rst doc/org.bluez.Device.rst \ doc/org.bluez.DeviceSet.rst doc/org.bluez.AgentManager.rst \ diff --git a/doc/iso.rst b/doc/iso.rst new file mode 100644 index 000000000000..d08b60d20865 --- /dev/null +++ b/doc/iso.rst @@ -0,0 +1,436 @@ +=== +iso +=== +------------- +ISO transport +------------- + +:Version: BlueZ +:Copyright: Free use of this software is granted under the terms of the GNU + Lesser General Public Licenses (LGPL). +:Date: July 2025 +:Manual section: 7 +:Manual group: Linux System Administration + +SYNOPSIS (since Linux 6.0 [experimental]) +========================================= + +.. code-block:: + + #include <sys/socket.h> + #include <bluetooth/bluetooth.h> + #include <bluetooth/iso.h> + + iso_socket = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_ISO); + +DESCRIPTION +=========== + +Bluetooth Isochronous Channels is a feature introduced in Bluetooth 5.2 that +allow for the transmission of multiple, synchronized audio streams between +devices. + +For unicast/multi-stream audio, connected isochronous group (CIG), and +connected isochronous stream (CIS) are used. A CIG is created by the central and +it can include one or more CISs. A CIS is a point-to-point data transportation +stream between a central and a certain peripheral, and is a bidirectional +communication protocol with acknowledgment. + +For broadcast audio, broadcast isochronous group (BIG) and broadcast isochronous +stream (BIS) are used. There are two types of devices: isochronous broadcaster +and synchronized receiver. A BIG is created by an isochronous broadcaster, and +it can include one or more BISs. A BIS is a one-to-many data transportation +stream. + +SOCKET ADDRESS +============== + +.. code-block:: + + struct sockaddr_iso_bc { + bdaddr_t bc_bdaddr; + uint8_t bc_bdaddr_type; + uint8_t bc_sid; + uint8_t bc_num_bis; + uint8_t bc_bis[ISO_MAX_NUM_BIS]; + }; + + struct sockaddr_iso { + sa_family_t iso_family; + bdaddr_t iso_bdaddr; + uint8_t iso_bdaddr_type; + struct sockaddr_iso_bc iso_bc[]; + }; + +Unicast example: + +.. code-block:: + + struct sockaddr_iso addr; + + memset(&addr, 0, sizeof(addr)); + addr.iso_family = AF_BLUETOOTH; + bacpy(&addr.iso_bdaddr, bdaddr); + addr.iso_bdaddr_type = BDADDR_LE_PUBLIC; + +Broadcast example: + +.. code-block:: + + struct sockaddr_iso *addr; + size_t addr_len; + + addr_len = sizeof(*addr) + sizeof(*addr->iso_bc); + + memset(addr, 0, addr_len); + addr->iso_family = AF_BLUETOOTH; + bacpy(&addr->iso_bdaddr, bdaddr); + addr->iso_bdaddr_type = BDADDR_LE_PUBLIC; + +Broadcast Source (Broadcaster) example: + +.. code-block:: + + struct sockaddr_iso *addr; + size_t addr_len; + + addr_len = sizeof(*addr) + sizeof(*addr->iso_bc); + + memset(addr, 0, addr_len); + addr->iso_family = AF_BLUETOOTH; + + /* Set address to BDADR_ANY(00:00:00:00:00:00) */ + bacpy(&addr->iso_bdaddr, BADDR_ANY); + addr->iso_bdaddr_type = BDADDR_LE_PUBLIC; + + /* Connect to Broadcast address */ + connect(iso_socket, (struct sockaddr *)addr, addr_len); + +Broadcast Sink (Receiver) example: + +.. code-block:: + + struct sockaddr_iso *addr; + size_t addr_len; + + addr_len = sizeof(*addr) + sizeof(*addr->iso_bc); + + memset(addr, 0, addr_len); + addr->iso_family = AF_BLUETOOTH; + + /* Set destination to Broadcaster address */ + bacpy(&addr->iso_bdaddr, bdaddr); + addr->iso_bdaddr_type = BDADDR_LE_PUBLIC; + + /* Bind to Broadcaster address */ + bind(iso_socket, (struct sockaddr *)addr, addr_len); + +SOCKET OPTIONS (SOL_BLUETOOTH) +============================== + +The socket options listed below can be set by using **setsockopt(2)** and read +with **getsockopt(2)** with the socket level set to SOL_BLUETOOTH. + +BT_SECURITY +----------- + +Channel security level, possible values: + +.. csv-table:: + :header: "Value", "Security Level", "Link Key Type", "Encryption" + :widths: auto + + **BT_SECURITY_SDP**, 0 (SDP Only), None, Not required + **BT_SECURITY_LOW**, 1 (Low), Unauthenticated, Not required + **BT_SECURITY_MEDIUM**, 2 (Medium - default), Unauthenticated, Desired + **BT_SECURITY_HIGH**, 3 (High), Authenticated, Required + **BT_SECURITY_FIPS** (since Linux 3.15), 4 (Secure Only), Authenticated (P-256 based Secure Simple Pairing and Secure Authentication), Required + +Example: + +.. code-block:: + + int level = BT_SECURITY_HIGH; + int err = setsockopt(iso_socket, SOL_BLUETOOTH, BT_SECURITY, &level, + sizeof(level)); + if (err == -1) { + perror("setsockopt"); + return 1; + } + +BT_DEFER_SETUP +-------------- + +Channel defer connection setup, this control if the connection procedure +needs to be authorized by userspace before responding which allows +authorization at profile level, possible values: + +.. csv-table:: + :header: "Value", "Description", "Authorization" + :widths: auto + + **0**, Disable (default), Not required + **1**, Enable, Required + +Example: + +.. code-block:: + + int defer_setup = 1; + int err = setsockopt(iso_socket, SOL_BLUETOOTH, BT_DEFER_SETUP, + &defer_setup, sizeof(defer_setup)); + if (err == -1) { + perror("setsockopt"); + return err; + } + + err = listen(iso_socket, 5); + if (err) { + perror("listen"); + return err; + } + + struct sockaddr_iso remote_addr = {0}; + socklen_t addr_len = sizeof(remote_addr); + int new_socket = accept(iso_socket, (struct sockaddr*)&remote_addr, + &addr_len); + if (new_socket < 0) { + perror("accept"); + return new_socket; + } + + /* To complete the connection setup of new_socket read 1 byte */ + char c; + struct pollfd pfd; + + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = new_socket; + pfd.events = POLLOUT; + + err = poll(&pfd, 1, 0); + if (err) { + perror("poll"); + return err; + } + + if (!(pfd.revents & POLLOUT)) { + err = read(sk, &c, 1); + if (err < 0) { + perror("read"); + return err; + } + } + +BT_PKT_STATUS +------------- + +Enable reporting packet status via `BT_SCM_PKT_STATUS` CMSG on +received packets. Possible values: + +.. csv-table:: + :header: "Value", "Description" + :widths: auto + + **0**, Disable (default) + **1**, Enable + + +:BT_SCM_PKT_STATUS: + + Level ``SOL_BLUETOOTH`` CMSG with data:: + + uint8_t pkt_status; + + The values are equal to the "Packet_Status_Flag" defined in + Core Specification v6.1, 5.4.5. HCI ISO Data packets: + + https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/Core-61/out/en/host-controller-interface/host-controller-interface-functional-specification.html#UUID-9b5fb085-278b-5084-ac33-bee2839abe6b + + .. csv-table:: + :header: "pkt_status", "Description" + :widths: auto + + **0x0**, Valid data. The complete SDU was received correctly. + **0x1**, Possibly invalid data. The contents of the ISO_SDU_Fragment + , may contain errors or part of the SDU may be missing. + , This is reported as "data with possible errors". + **0x2**, Part(s) of the SDU were not received correctly. + , This is reported as "lost data". + +SOCKET OPTIONS (SOL_SOCKET) +=========================== + +``SOL_SOCKET`` level socket options that modify generic socket +features (``SO_SNDBUF``, ``SO_RCVBUF``, etc.) have their usual +meaning, see **socket(7)**. + +The ``SOL_SOCKET`` level ISO socket options that have +Bluetooth-specific handling in kernel are listed below. + +SO_TIMESTAMPING, SO_TIMESTAMP, SO_TIMESTAMPNS +--------------------------------------------- + +See https://docs.kernel.org/networking/timestamping.html + +For ISO sockets, software RX timestamps are supported. Software TX +timestamps (SOF_TIMESTAMPING_TX_SOFTWARE) are supported since +Linux 6.15. + +The software RX timestamp is the time when the kernel received the +packet from the controller driver. + +The ``SCM_TSTAMP_SND`` timestamp is emitted when packet is sent to the +controller driver. + +The ``SCM_TSTAMP_COMPLETION`` timestamp is emitted when controller +reports the packet completed. Completion timestamps are only +supported on controllers that have ISO flow control. Other TX +timestamp types are not supported. + +You can use ``SIOCETHTOOL`` to query supported flags. + +The timestamps are in ``CLOCK_REALTIME`` time. + +Example (Enable RX timestamping): + +.. code-block:: + + int flags = SOF_TIMESTAMPING_SOFTWARE | + SOF_TIMESTAMPING_RX_SOFTWARE; + setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, &flags, sizeof(flags)); + +Example (Read packet and its RX timestamp): + +.. code-block:: + + char data_buf[256]; + union { + char buf[CMSG_SPACE(sizeof(struct scm_timestamping))]; + struct cmsghdr align; + } control; + struct iovec data = { + .iov_base = data_buf, + .iov_len = sizeof(data_buf), + }; + struct msghdr msg = { + .msg_iov = &data, + .msg_iovlen = 1, + .msg_control = control.buf, + .msg_controllen = sizeof(control.buf), + }; + struct scm_timestamping tss; + + res = recvmsg(fd, &msg, MSG_ERRQUEUE | MSG_DONTWAIT); + if (res < 0) + goto error; + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPING) + memcpy(&tss, CMSG_DATA(cmsg), sizeof(tss)); + } + + tstamp_clock_realtime = tss.ts[0]; + +Example (Enable TX timestamping): + +.. code-block:: + + int flags = SOF_TIMESTAMPING_SOFTWARE | + SOF_TIMESTAMPING_TX_SOFTWARE | + SOF_TIMESTAMPING_OPT_ID; + setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, &flags, sizeof(flags)); + +Example (Read TX timestamps): + +.. code-block:: + + union { + char buf[CMSG_SPACE(sizeof(struct scm_timestamping))]; + struct cmsghdr align; + } control; + struct iovec data = { + .iov_base = NULL, + .iov_len = 0 + }; + struct msghdr msg = { + .msg_iov = &data, + .msg_iovlen = 1, + .msg_control = control.buf, + .msg_controllen = sizeof(control.buf), + }; + struct cmsghdr *cmsg; + struct scm_timestamping tss; + struct sock_extended_err serr; + int res; + + res = recvmsg(fd, &msg, MSG_ERRQUEUE | MSG_DONTWAIT); + if (res < 0) + goto error; + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPING) + memcpy(&tss, CMSG_DATA(cmsg), sizeof(tss)); + else if (cmsg->cmsg_level == SOL_BLUETOOTH && cmsg->cmsg_type == BT_SCM_ERROR) + memcpy(&serr, CMSG_DATA(cmsg), sizeof(serr)); + } + + tstamp_clock_realtime = tss.ts[0]; + tstamp_type = serr->ee_info; /* SCM_TSTAMP_SND or SCM_TSTAMP_COMPLETION */ + tstamp_seqnum = serr->ee_data; + + +IOCTLS +====== + +The following ioctls with operation specific for ISO sockets are +available. + +SIOCETHTOOL (since Linux 6.16-rc1) +---------------------------------- + +Supports only command `ETHTOOL_GET_TS_INFO`, which may be used to +query supported `SOF_TIMESTAMPING_*` flags. The +`SOF_TIMESTAMPING_OPT_*` flags are always available as applicable. + +Example: + +.. code-block:: + + #include <linux/ethtool.h> + #include <linux/sockios.h> + #include <net/if.h> + #include <sys/socket.h> + #include <sys/ioctl.h> + + ... + + struct ifreq ifr = {}; + struct ethtool_ts_info cmd = {}; + int sk; + + snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "hci0"); + ifr.ifr_data = (void *)&cmd; + cmd.cmd = ETHTOOL_GET_TS_INFO; + + sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_ISO); + if (sk < 0) + goto error; + if (ioctl(sk, SIOCETHTOOL, &ifr)) + goto error; + + sof_available = cmd.so_timestamping; + +RESOURCES +========= + +http://www.bluez.org + +REPORTING BUGS +============== + +linux-bluetooth@xxxxxxxxxxxxxxx + +SEE ALSO +======== + +socket(7), isotest(1) -- 2.50.0