Commit 09572fca authored by Luiz Augusto von Dentz's avatar Luiz Augusto von Dentz Committed by Marcel Holtmann
Browse files

Bluetooth: hci_sock: Add support for BT_{SND,RCV}BUF

This adds support for BT_{SND,RCV}BUF so userspace can set MTU based on
the channel usage.

Fixes: https://github.com/bluez/bluez/issues/201



Signed-off-by: default avatarLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Signed-off-by: default avatarMarcel Holtmann <marcel@holtmann.org>
parent 01ce70b0
Loading
Loading
Loading
Loading
+91 −11
Original line number Diff line number Diff line
@@ -57,6 +57,7 @@ struct hci_pinfo {
	unsigned long     flags;
	__u32             cookie;
	char              comm[TASK_COMM_LEN];
	__u16             mtu;
};

static struct hci_dev *hci_hdev_from_sock(struct sock *sk)
@@ -1374,6 +1375,10 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
		break;
	}

	/* Default MTU to HCI_MAX_FRAME_SIZE if not set */
	if (!hci_pi(sk)->mtu)
		hci_pi(sk)->mtu = HCI_MAX_FRAME_SIZE;

	sk->sk_state = BT_BOUND;

done:
@@ -1719,7 +1724,7 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
	if (flags & ~(MSG_DONTWAIT | MSG_NOSIGNAL | MSG_ERRQUEUE | MSG_CMSG_COMPAT))
		return -EINVAL;

	if (len < 4 || len > HCI_MAX_FRAME_SIZE)
	if (len < 4 || len > hci_pi(sk)->mtu)
		return -EINVAL;

	buf = kmalloc(len, GFP_KERNEL);
@@ -1849,7 +1854,7 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
	goto done;
}

static int hci_sock_setsockopt(struct socket *sock, int level, int optname,
static int hci_sock_setsockopt_old(struct socket *sock, int level, int optname,
				   sockptr_t optval, unsigned int len)
{
	struct hci_ufilter uf = { .opcode = 0 };
@@ -1858,9 +1863,6 @@ static int hci_sock_setsockopt(struct socket *sock, int level, int optname,

	BT_DBG("sk %p, opt %d", sk, optname);

	if (level != SOL_HCI)
		return -ENOPROTOOPT;

	lock_sock(sk);

	if (hci_pi(sk)->channel != HCI_CHANNEL_RAW) {
@@ -1935,7 +1937,55 @@ static int hci_sock_setsockopt(struct socket *sock, int level, int optname,
	return err;
}

static int hci_sock_getsockopt(struct socket *sock, int level, int optname,
static int hci_sock_setsockopt(struct socket *sock, int level, int optname,
			       sockptr_t optval, unsigned int len)
{
	struct sock *sk = sock->sk;
	int err = 0, opt = 0;

	BT_DBG("sk %p, opt %d", sk, optname);

	if (level == SOL_HCI)
		return hci_sock_setsockopt_old(sock, level, optname, optval,
					       len);

	if (level != SOL_BLUETOOTH)
		return -ENOPROTOOPT;

	lock_sock(sk);

	switch (optname) {
	case BT_SNDMTU:
	case BT_RCVMTU:
		switch (hci_pi(sk)->channel) {
		/* Don't allow changing MTU for channels that are meant for HCI
		 * traffic only.
		 */
		case HCI_CHANNEL_RAW:
		case HCI_CHANNEL_USER:
			err = -ENOPROTOOPT;
			goto done;
		}

		if (copy_from_sockptr(&opt, optval, sizeof(u16))) {
			err = -EFAULT;
			break;
		}

		hci_pi(sk)->mtu = opt;
		break;

	default:
		err = -ENOPROTOOPT;
		break;
	}

done:
	release_sock(sk);
	return err;
}

static int hci_sock_getsockopt_old(struct socket *sock, int level, int optname,
				   char __user *optval, int __user *optlen)
{
	struct hci_ufilter uf;
@@ -1944,9 +1994,6 @@ static int hci_sock_getsockopt(struct socket *sock, int level, int optname,

	BT_DBG("sk %p, opt %d", sk, optname);

	if (level != SOL_HCI)
		return -ENOPROTOOPT;

	if (get_user(len, optlen))
		return -EFAULT;

@@ -2004,6 +2051,39 @@ static int hci_sock_getsockopt(struct socket *sock, int level, int optname,
	return err;
}

static int hci_sock_getsockopt(struct socket *sock, int level, int optname,
			       char __user *optval, int __user *optlen)
{
	struct sock *sk = sock->sk;
	int err = 0;

	BT_DBG("sk %p, opt %d", sk, optname);

	if (level == SOL_HCI)
		return hci_sock_getsockopt_old(sock, level, optname, optval,
					       optlen);

	if (level != SOL_BLUETOOTH)
		return -ENOPROTOOPT;

	lock_sock(sk);

	switch (optname) {
	case BT_SNDMTU:
	case BT_RCVMTU:
		if (put_user(hci_pi(sk)->mtu, (u16 __user *)optval))
			err = -EFAULT;
		break;

	default:
		err = -ENOPROTOOPT;
		break;
	}

	release_sock(sk);
	return err;
}

static const struct proto_ops hci_sock_ops = {
	.family		= PF_BLUETOOTH,
	.owner		= THIS_MODULE,