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
2 changed files with 120 additions and 1 deletions
|
@ -139,6 +139,14 @@ struct bt_voice {
|
||||||
#define BT_PHY_LE_CODED_TX 0x00002000
|
#define BT_PHY_LE_CODED_TX 0x00002000
|
||||||
#define BT_PHY_LE_CODED_RX 0x00004000
|
#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)
|
__printf(1, 2)
|
||||||
void bt_info(const char *fmt, ...);
|
void bt_info(const char *fmt, ...);
|
||||||
__printf(1, 2)
|
__printf(1, 2)
|
||||||
|
|
|
@ -395,6 +395,24 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr,
|
||||||
return sizeof(struct sockaddr_l2);
|
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,
|
static int l2cap_sock_getsockopt_old(struct socket *sock, int optname,
|
||||||
char __user *optval, int __user *optlen)
|
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_security sec;
|
||||||
struct bt_power pwr;
|
struct bt_power pwr;
|
||||||
u32 phys;
|
u32 phys;
|
||||||
int len, err = 0;
|
int len, mode, err = 0;
|
||||||
|
|
||||||
BT_DBG("sk %p", sk);
|
BT_DBG("sk %p", sk);
|
||||||
|
|
||||||
|
@ -638,6 +656,27 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
|
||||||
err = -EFAULT;
|
err = -EFAULT;
|
||||||
break;
|
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:
|
default:
|
||||||
err = -ENOPROTOOPT;
|
err = -ENOPROTOOPT;
|
||||||
break;
|
break;
|
||||||
|
@ -780,6 +819,45 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname,
|
||||||
return err;
|
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,
|
static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
|
||||||
char __user *optval, unsigned int optlen)
|
char __user *optval, unsigned int optlen)
|
||||||
{
|
{
|
||||||
|
@ -985,6 +1063,39 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
|
||||||
|
|
||||||
break;
|
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:
|
default:
|
||||||
err = -ENOPROTOOPT;
|
err = -ENOPROTOOPT;
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Add table
Reference in a new issue