Hi, ti, 2025-07-15 kello 14:17 -0400, Luiz Augusto von Dentz kirjoitti: > Hi Pauli, > > On Tue, Jul 15, 2025 at 2:07 PM Luiz Augusto von Dentz > <luiz.dentz@xxxxxxxxx> wrote: > > > > 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. > > Completion timestamps are always available for ISO, as currently kernel only does flow-controlled ISO. > > 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 > > > > Have a look if the above makes sense, I didn't include BT_PKT_SEQNUM > since that is sort of new, but if you feel that should be included > from the start I just need an example to go with it. Looks right to me. BT_PKT_SEQNUM if added would be like BT_PKT_SEQNUM ------------- Enable reporting packet ISO sequence number via `BT_SCM_PKT_SEQNUM` CMSG on received packets. Possible values: .. csv-table:: :header: "Value", "Description" :widths: auto **0**, Disable (default) **1**, Enable :BT_SCM_PKT_SEQNUM: Level ``SOL_BLUETOOTH`` CMSG with data:: uint16_t pkt_seqnum; The values are equal to the "Packet_Sequence_Number" 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 Example (Enable sequence numbers): .. code-block:: uint32_t opt = 1; if (setsockopt(fd, SOL_BLUETOOTH, BT_PKT_SEQNUM, &opt, sizeof(opt)) < 0) goto error; Example (Read packet and its sequence number): .. code-block:: char data_buf[256]; uint16_t seqnum; union { char buf[CMSG_SPACE(sizeof(uint16_t))]; 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, 0); if (res < 0) goto error; for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level == SOL_BLUETOOTH && cmsg->cmsg_type == BT_PKT_SEQNUM) memcpy(&seqnum, CMSG_DATA(cmsg), sizeof(seqnum)); } -- Pauli Virtanen