Bluetooth: add support for SIOCETHTOOL ETHTOOL_GET_TS_INFO
authorPauli Virtanen <pav@iki.fi>
Sun, 27 Apr 2025 11:27:25 +0000 (14:27 +0300)
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Wed, 21 May 2025 14:28:51 +0000 (10:28 -0400)
Bluetooth needs some way for user to get supported so_timestamping flags
for the different socket types.

Use SIOCETHTOOL API for this purpose. As hci_dev is not associated with
struct net_device, the existing implementation can't be reused, so we
add a small one here.

Add support (only) for ETHTOOL_GET_TS_INFO command. The API differs
slightly from netdev in that the result depends also on socket type.

Signed-off-by: Pauli Virtanen <pav@iki.fi>
Acked-by: Willem de Bruijn <willemb@google.com>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
include/net/bluetooth/bluetooth.h
net/bluetooth/af_bluetooth.c
net/bluetooth/hci_conn.c

index bbefde319f95e6ef97b981af39cc4559fc17e0c8..114299bd8b9878f70ca0286ccec1df2cd31735fe 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/poll.h>
 #include <net/sock.h>
 #include <linux/seq_file.h>
+#include <linux/ethtool.h>
 
 #define BT_SUBSYS_VERSION      2
 #define BT_SUBSYS_REVISION     22
@@ -448,6 +449,9 @@ void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status,
                          hci_req_complete_t *req_complete,
                          hci_req_complete_skb_t *req_complete_skb);
 
+int hci_ethtool_ts_info(unsigned int index, int sk_proto,
+                       struct kernel_ethtool_ts_info *ts_info);
+
 #define HCI_REQ_START  BIT(0)
 #define HCI_REQ_SKB    BIT(1)
 
index 0b4d0a8bd361416c3417dd4407e38d00dbfa72b1..6ad2f72f53f4e546dd4a3267c8f3d87c8f4118ee 100644 (file)
@@ -34,6 +34,9 @@
 #include <net/bluetooth/bluetooth.h>
 #include <linux/proc_fs.h>
 
+#include <linux/ethtool.h>
+#include <linux/sockios.h>
+
 #include "leds.h"
 #include "selftest.h"
 
@@ -563,6 +566,86 @@ __poll_t bt_sock_poll(struct file *file, struct socket *sock,
 }
 EXPORT_SYMBOL(bt_sock_poll);
 
+static int bt_ethtool_get_ts_info(struct sock *sk, unsigned int index,
+                                 void __user *useraddr)
+{
+       struct ethtool_ts_info info;
+       struct kernel_ethtool_ts_info ts_info = {};
+       int ret;
+
+       ret = hci_ethtool_ts_info(index, sk->sk_protocol, &ts_info);
+       if (ret == -ENODEV)
+               return ret;
+       else if (ret < 0)
+               return -EIO;
+
+       memset(&info, 0, sizeof(info));
+
+       info.cmd = ETHTOOL_GET_TS_INFO;
+       info.so_timestamping = ts_info.so_timestamping;
+       info.phc_index = ts_info.phc_index;
+       info.tx_types = ts_info.tx_types;
+       info.rx_filters = ts_info.rx_filters;
+
+       if (copy_to_user(useraddr, &info, sizeof(info)))
+               return -EFAULT;
+
+       return 0;
+}
+
+static int bt_ethtool(struct sock *sk, const struct ifreq *ifr,
+                     void __user *useraddr)
+{
+       unsigned int index;
+       u32 ethcmd;
+       int n;
+
+       if (copy_from_user(&ethcmd, useraddr, sizeof(ethcmd)))
+               return -EFAULT;
+
+       if (sscanf(ifr->ifr_name, "hci%u%n", &index, &n) != 1 ||
+           n != strlen(ifr->ifr_name))
+               return -ENODEV;
+
+       switch (ethcmd) {
+       case ETHTOOL_GET_TS_INFO:
+               return bt_ethtool_get_ts_info(sk, index, useraddr);
+       }
+
+       return -EOPNOTSUPP;
+}
+
+static int bt_dev_ioctl(struct socket *sock, unsigned int cmd, void __user *arg)
+{
+       struct sock *sk = sock->sk;
+       struct ifreq ifr = {};
+       void __user *data;
+       char *colon;
+       int ret = -ENOIOCTLCMD;
+
+       if (get_user_ifreq(&ifr, &data, arg))
+               return -EFAULT;
+
+       ifr.ifr_name[IFNAMSIZ - 1] = 0;
+       colon = strchr(ifr.ifr_name, ':');
+       if (colon)
+               *colon = 0;
+
+       switch (cmd) {
+       case SIOCETHTOOL:
+               ret = bt_ethtool(sk, &ifr, data);
+               break;
+       }
+
+       if (colon)
+               *colon = ':';
+
+       if (put_user_ifreq(&ifr, arg))
+               return -EFAULT;
+
+       return ret;
+}
+
 int bt_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
 {
        struct sock *sk = sock->sk;
@@ -595,6 +678,10 @@ int bt_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
                err = put_user(amount, (int __user *)arg);
                break;
 
+       case SIOCETHTOOL:
+               err = bt_dev_ioctl(sock, cmd, (void __user *)arg);
+               break;
+
        default:
                err = -ENOIOCTLCMD;
                break;
index c0207812f43282dca4be6244761aa74ffd99807d..59a56425521fee4548f4746cc906c37ecd7be435 100644 (file)
@@ -3049,3 +3049,36 @@ u8 *hci_conn_key_enc_size(struct hci_conn *conn)
 
        return NULL;
 }
+
+int hci_ethtool_ts_info(unsigned int index, int sk_proto,
+                       struct kernel_ethtool_ts_info *info)
+{
+       struct hci_dev *hdev;
+
+       hdev = hci_dev_get(index);
+       if (!hdev)
+               return -ENODEV;
+
+       info->so_timestamping =
+               SOF_TIMESTAMPING_RX_SOFTWARE |
+               SOF_TIMESTAMPING_SOFTWARE;
+       info->phc_index = -1;
+       info->tx_types = BIT(HWTSTAMP_TX_OFF);
+       info->rx_filters = BIT(HWTSTAMP_FILTER_NONE);
+
+       switch (sk_proto) {
+       case BTPROTO_ISO:
+       case BTPROTO_L2CAP:
+               info->so_timestamping |= SOF_TIMESTAMPING_TX_SOFTWARE;
+               info->so_timestamping |= SOF_TIMESTAMPING_TX_COMPLETION;
+               break;
+       case BTPROTO_SCO:
+               info->so_timestamping |= SOF_TIMESTAMPING_TX_SOFTWARE;
+               if (hci_dev_test_flag(hdev, HCI_SCO_FLOWCTL))
+                       info->so_timestamping |= SOF_TIMESTAMPING_TX_COMPLETION;
+               break;
+       }
+
+       hci_dev_put(hdev);
+       return 0;
+}