Re: [PATCH BlueZ v1] doc: Add initial ISO(7) documentation

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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





[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux