Add explanations and examples for SCO and L2CAP timestamping-related features. --- doc/l2cap.rst | 166 ++++++++++++++++++++++++++++++++++++++++++++++++- doc/sco.rst | 168 +++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 330 insertions(+), 4 deletions(-) diff --git a/doc/l2cap.rst b/doc/l2cap.rst index 0b6f1bb99..d53438924 100644 --- a/doc/l2cap.rst +++ b/doc/l2cap.rst @@ -67,8 +67,8 @@ Example: addr.l2_bdaddr_type = bdaddr_type; -SOCKET OPTIONS -============== +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. @@ -242,6 +242,168 @@ Channel Mode, possible values: **BT_MODE_LE_FLOWCTL**, 0x03, Credit based flow control mode, LE **BT_MODE_EXT_FLOWCTL**, 0x04, Extended Credit based flow control mode, Any + +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 L2CAP 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 L2CAP sockets, software RX timestamps are supported. Software TX +timestamps (SOF_TIMESTAMPING_TX_SOFTWARE, +SOF_TIMESTAMPING_TX_COMPLETION) 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. 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_TX_COMPLETION | + SOF_TIMESTAMPING_OPT_ID; + setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, &flags, sizeof(flags)); + +Example (Read TX timestamps): + +.. code-block:: + + union { + char buf[2 * 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 L2CAP 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_L2CAP); + if (sk < 0) + goto error; + if (ioctl(sk, SIOCETHTOOL, &ifr)) + goto error; + + sof_available = cmd.so_timestamping; + RESOURCES ========= diff --git a/doc/sco.rst b/doc/sco.rst index a8fe3e87f..766a1bf1e 100644 --- a/doc/sco.rst +++ b/doc/sco.rst @@ -55,8 +55,8 @@ Example: addr.sco_family = AF_BLUETOOTH; bacpy(&addr.sco_bdaddr, bdaddr); -SOCKET OPTIONS -============== +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. @@ -244,6 +244,170 @@ Example: return 1; } + +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 SCO 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 SCO 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 SCO 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 SCO 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_SCO); + if (sk < 0) + goto error; + if (ioctl(sk, SIOCETHTOOL, &ifr)) + goto error; + + sof_available = cmd.so_timestamping; + RESOURCES ========= -- 2.50.1