Bluetooth: Add BT_MODE socket option
This adds BT_MODE socket option which can be used to set L2CAP modes, including modes only supported over LE which were not supported using the L2CAP_OPTIONS. Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
This commit is contained in:
parent
b86b0b150f
commit
3ee7b7cd83
|
@ -139,6 +139,14 @@ struct bt_voice {
|
|||
#define BT_PHY_LE_CODED_TX 0x00002000
|
||||
#define BT_PHY_LE_CODED_RX 0x00004000
|
||||
|
||||
#define BT_MODE 15
|
||||
|
||||
#define BT_MODE_BASIC 0x00
|
||||
#define BT_MODE_ERTM 0x01
|
||||
#define BT_MODE_STREAMING 0x02
|
||||
#define BT_MODE_LE_FLOWCTL 0x03
|
||||
#define BT_MODE_EXT_FLOWCTL 0x04
|
||||
|
||||
__printf(1, 2)
|
||||
void bt_info(const char *fmt, ...);
|
||||
__printf(1, 2)
|
||||
|
|
|
@ -395,6 +395,24 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr,
|
|||
return sizeof(struct sockaddr_l2);
|
||||
}
|
||||
|
||||
static int l2cap_get_mode(struct l2cap_chan *chan)
|
||||
{
|
||||
switch (chan->mode) {
|
||||
case L2CAP_MODE_BASIC:
|
||||
return BT_MODE_BASIC;
|
||||
case L2CAP_MODE_ERTM:
|
||||
return BT_MODE_ERTM;
|
||||
case L2CAP_MODE_STREAMING:
|
||||
return BT_MODE_STREAMING;
|
||||
case L2CAP_MODE_LE_FLOWCTL:
|
||||
return BT_MODE_LE_FLOWCTL;
|
||||
case L2CAP_MODE_EXT_FLOWCTL:
|
||||
return BT_MODE_EXT_FLOWCTL;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int l2cap_sock_getsockopt_old(struct socket *sock, int optname,
|
||||
char __user *optval, int __user *optlen)
|
||||
{
|
||||
|
@ -522,7 +540,7 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
|
|||
struct bt_security sec;
|
||||
struct bt_power pwr;
|
||||
u32 phys;
|
||||
int len, err = 0;
|
||||
int len, mode, err = 0;
|
||||
|
||||
BT_DBG("sk %p", sk);
|
||||
|
||||
|
@ -638,6 +656,27 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
|
|||
err = -EFAULT;
|
||||
break;
|
||||
|
||||
case BT_MODE:
|
||||
if (!enable_ecred) {
|
||||
err = -ENOPROTOOPT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
mode = l2cap_get_mode(chan);
|
||||
if (mode < 0) {
|
||||
err = mode;
|
||||
break;
|
||||
}
|
||||
|
||||
if (put_user(mode, (u8 __user *) optval))
|
||||
err = -EFAULT;
|
||||
break;
|
||||
|
||||
default:
|
||||
err = -ENOPROTOOPT;
|
||||
break;
|
||||
|
@ -780,6 +819,45 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname,
|
|||
return err;
|
||||
}
|
||||
|
||||
static int l2cap_set_mode(struct l2cap_chan *chan, u8 mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case BT_MODE_BASIC:
|
||||
if (bdaddr_type_is_le(chan->src_type))
|
||||
return -EINVAL;
|
||||
mode = L2CAP_MODE_BASIC;
|
||||
clear_bit(CONF_STATE2_DEVICE, &chan->conf_state);
|
||||
break;
|
||||
case BT_MODE_ERTM:
|
||||
if (!disable_ertm || bdaddr_type_is_le(chan->src_type))
|
||||
return -EINVAL;
|
||||
mode = L2CAP_MODE_ERTM;
|
||||
break;
|
||||
case BT_MODE_STREAMING:
|
||||
if (!disable_ertm || bdaddr_type_is_le(chan->src_type))
|
||||
return -EINVAL;
|
||||
mode = L2CAP_MODE_STREAMING;
|
||||
break;
|
||||
case BT_MODE_LE_FLOWCTL:
|
||||
if (!bdaddr_type_is_le(chan->src_type))
|
||||
return -EINVAL;
|
||||
mode = L2CAP_MODE_LE_FLOWCTL;
|
||||
break;
|
||||
case BT_MODE_EXT_FLOWCTL:
|
||||
/* TODO: Add support for ECRED PDUs to BR/EDR */
|
||||
if (!bdaddr_type_is_le(chan->src_type))
|
||||
return -EINVAL;
|
||||
mode = L2CAP_MODE_EXT_FLOWCTL;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
chan->mode = mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
|
||||
char __user *optval, unsigned int optlen)
|
||||
{
|
||||
|
@ -985,6 +1063,39 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
|
|||
|
||||
break;
|
||||
|
||||
case BT_MODE:
|
||||
if (!enable_ecred) {
|
||||
err = -ENOPROTOOPT;
|
||||
break;
|
||||
}
|
||||
|
||||
BT_DBG("sk->sk_state %u", sk->sk_state);
|
||||
|
||||
if (sk->sk_state != BT_BOUND) {
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (get_user(opt, (u8 __user *) optval)) {
|
||||
err = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
BT_DBG("opt %u", opt);
|
||||
|
||||
err = l2cap_set_mode(chan, opt);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
BT_DBG("mode 0x%2.2x", chan->mode);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
err = -ENOPROTOOPT;
|
||||
break;
|
||||
|
|
Loading…
Reference in New Issue