Commit dd0ccf85 authored by Pauli Virtanen's avatar Pauli Virtanen Committed by Luiz Augusto von Dentz
Browse files

Bluetooth: add support for SIOCETHTOOL ETHTOOL_GET_TS_INFO



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: default avatarPauli Virtanen <pav@iki.fi>
Acked-by: default avatarWillem de Bruijn <willemb@google.com>
Signed-off-by: default avatarLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
parent 19037750
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -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)

+87 −0
Original line number Diff line number Diff line
@@ -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;
+33 −0
Original line number Diff line number Diff line
@@ -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;
}