提交 8962ee74 编写于 作者: J Johan Hedberg 提交者: Gustavo F. Padovan

Bluetooth: Add disconnect managment command

This patch adds a disconnect command to the managment interface. Using
this command user space is able to force the disconnection of connected
devices. The command maps directly to the Disconnect HCI command.
Signed-off-by: NJohan Hedberg <johan.hedberg@nokia.com>
Signed-off-by: NGustavo F. Padovan <padovan@profusion.mobi>
上级 f7520543
master alk-4.19.24 alk-4.19.30 alk-4.19.34 alk-4.19.36 alk-4.19.43 alk-4.19.48 alk-4.19.57 ck-4.19.67 ck-4.19.81 ck-4.19.91 github/fork/deepanshu1422/fix-typo-in-comment github/fork/haosdent/fix-typo linux-next v4.19.91 v4.19.90 v4.19.89 v4.19.88 v4.19.87 v4.19.86 v4.19.85 v4.19.84 v4.19.83 v4.19.82 v4.19.81 v4.19.80 v4.19.79 v4.19.78 v4.19.77 v4.19.76 v4.19.75 v4.19.74 v4.19.73 v4.19.72 v4.19.71 v4.19.70 v4.19.69 v4.19.68 v4.19.67 v4.19.66 v4.19.65 v4.19.64 v4.19.63 v4.19.62 v4.19.61 v4.19.60 v4.19.59 v4.19.58 v4.19.57 v4.19.56 v4.19.55 v4.19.54 v4.19.53 v4.19.52 v4.19.51 v4.19.50 v4.19.49 v4.19.48 v4.19.47 v4.19.46 v4.19.45 v4.19.44 v4.19.43 v4.19.42 v4.19.41 v4.19.40 v4.19.39 v4.19.38 v4.19.37 v4.19.36 v4.19.35 v4.19.34 v4.19.33 v4.19.32 v4.19.31 v4.19.30 v4.19.29 v4.19.28 v4.19.27 v4.19.26 v4.19.25 v4.19.24 v4.19.23 v4.19.22 v4.19.21 v4.19.20 v4.19.19 v4.19.18 v4.19.17 v4.19.16 v4.19.15 v4.19.14 v4.19.13 v4.19.12 v4.19.11 v4.19.10 v4.19.9 v4.19.8 v4.19.7 v4.19.6 v4.19.5 v4.19.4 v4.19.3 v4.19.2 v4.19.1 v4.19 v4.19-rc8 v4.19-rc7 v4.19-rc6 v4.19-rc5 v4.19-rc4 v4.19-rc3 v4.19-rc2 v4.19-rc1 ck-release-21 ck-release-20 ck-release-19.2 ck-release-19.1 ck-release-19 ck-release-18 ck-release-17.2 ck-release-17.1 ck-release-17 ck-release-16 ck-release-15.1 ck-release-15 ck-release-14 ck-release-13.2 ck-release-13 ck-release-12 ck-release-11 ck-release-10 ck-release-9 ck-release-7 alk-release-15 alk-release-14 alk-release-13.2 alk-release-13 alk-release-12 alk-release-11 alk-release-10 alk-release-9 alk-release-7
无相关合并请求
......@@ -716,6 +716,7 @@ int mgmt_connectable(u16 index, u8 connectable);
int mgmt_new_key(u16 index, struct link_key *key, u8 old_key_type);
int mgmt_connected(u16 index, bdaddr_t *bdaddr);
int mgmt_disconnected(u16 index, bdaddr_t *bdaddr);
int mgmt_disconnect_failed(u16 index);
/* HCI info for socket */
#define hci_pi(sk) ((struct hci_pinfo *) sk)
......
......@@ -120,6 +120,16 @@ struct mgmt_cp_remove_key {
__u8 disconnect;
} __packed;
#define MGMT_OP_DISCONNECT 0x000F
struct mgmt_cp_disconnect {
__le16 index;
bdaddr_t bdaddr;
} __packed;
struct mgmt_rp_disconnect {
__le16 index;
bdaddr_t bdaddr;
} __packed;
#define MGMT_EV_CMD_COMPLETE 0x0001
struct mgmt_ev_cmd_complete {
__le16 opcode;
......
......@@ -1264,8 +1264,10 @@ static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff
BT_DBG("%s status %d", hdev->name, ev->status);
if (ev->status)
if (ev->status) {
mgmt_disconnect_failed(hdev->id);
return;
}
hci_dev_lock(hdev);
......@@ -1680,6 +1682,11 @@ static inline void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_cs_exit_sniff_mode(hdev, ev->status);
break;
case HCI_OP_DISCONNECT:
if (ev->status != 0)
mgmt_disconnect_failed(hdev->id);
break;
default:
BT_DBG("%s opcode 0x%x", hdev->name, opcode);
break;
......
......@@ -887,6 +887,60 @@ static int remove_key(struct sock *sk, unsigned char *data, u16 len)
return err;
}
static int disconnect(struct sock *sk, unsigned char *data, u16 len)
{
struct hci_dev *hdev;
struct mgmt_cp_disconnect *cp;
struct hci_cp_disconnect dc;
struct hci_conn *conn;
u16 dev_id;
int err;
BT_DBG("");
cp = (void *) data;
dev_id = get_unaligned_le16(&cp->index);
hdev = hci_dev_get(dev_id);
if (!hdev)
return cmd_status(sk, MGMT_OP_DISCONNECT, ENODEV);
hci_dev_lock_bh(hdev);
if (!test_bit(HCI_UP, &hdev->flags)) {
err = cmd_status(sk, MGMT_OP_DISCONNECT, ENETDOWN);
goto failed;
}
if (mgmt_pending_find(MGMT_OP_DISCONNECT, dev_id)) {
err = cmd_status(sk, MGMT_OP_DISCONNECT, EBUSY);
goto failed;
}
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr);
if (!conn) {
err = cmd_status(sk, MGMT_OP_DISCONNECT, ENOTCONN);
goto failed;
}
err = mgmt_pending_add(sk, MGMT_OP_DISCONNECT, dev_id, data, len);
if (err < 0)
goto failed;
put_unaligned_le16(conn->handle, &dc.handle);
dc.reason = 0x13; /* Remote User Terminated Connection */
err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(dc), &dc);
if (err < 0)
mgmt_pending_remove(MGMT_OP_DISCONNECT, dev_id);
failed:
hci_dev_unlock_bh(hdev);
hci_dev_put(hdev);
return err;
}
int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
{
unsigned char *buf;
......@@ -957,6 +1011,9 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
case MGMT_OP_REMOVE_KEY:
err = remove_key(sk, buf + sizeof(*hdr), len);
break;
case MGMT_OP_DISCONNECT:
err = disconnect(sk, buf + sizeof(*hdr), len);
break;
default:
BT_DBG("Unknown op %u", opcode);
err = cmd_status(sk, opcode, 0x01);
......@@ -1101,12 +1158,72 @@ int mgmt_connected(u16 index, bdaddr_t *bdaddr)
return mgmt_event(MGMT_EV_CONNECTED, &ev, sizeof(ev), NULL);
}
static void disconnect_rsp(struct pending_cmd *cmd, void *data)
{
struct mgmt_cp_disconnect *cp = cmd->cmd;
struct sock **sk = data;
struct sk_buff *skb;
struct mgmt_hdr *hdr;
struct mgmt_ev_cmd_complete *ev;
struct mgmt_rp_disconnect *rp;
skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + sizeof(*rp), GFP_ATOMIC);
if (!skb)
return;
hdr = (void *) skb_put(skb, sizeof(*hdr));
hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
hdr->len = cpu_to_le16(sizeof(*ev) + sizeof(*rp));
ev = (void *) skb_put(skb, sizeof(*ev));
put_unaligned_le16(MGMT_OP_DISCONNECT, &ev->opcode);
rp = (void *) skb_put(skb, sizeof(*rp));
put_unaligned_le16(cmd->index, &rp->index);
bacpy(&rp->bdaddr, &cp->bdaddr);
if (sock_queue_rcv_skb(cmd->sk, skb) < 0)
kfree_skb(skb);
*sk = cmd->sk;
sock_hold(*sk);
list_del(&cmd->list);
mgmt_pending_free(cmd);
}
int mgmt_disconnected(u16 index, bdaddr_t *bdaddr)
{
struct mgmt_ev_disconnected ev;
struct sock *sk = NULL;
int err;
mgmt_pending_foreach(MGMT_OP_DISCONNECT, index, disconnect_rsp, &sk);
put_unaligned_le16(index, &ev.index);
bacpy(&ev.bdaddr, bdaddr);
return mgmt_event(MGMT_EV_DISCONNECTED, &ev, sizeof(ev), NULL);
err = mgmt_event(MGMT_EV_DISCONNECTED, &ev, sizeof(ev), sk);
if (sk)
sock_put(sk);
return err;
}
int mgmt_disconnect_failed(u16 index)
{
struct pending_cmd *cmd;
int err;
cmd = mgmt_pending_find(MGMT_OP_DISCONNECT, index);
if (!cmd)
return -ENOENT;
err = cmd_status(cmd->sk, MGMT_OP_DISCONNECT, EIO);
list_del(&cmd->list);
mgmt_pending_free(cmd);
return err;
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册
反馈
建议
客服 返回
顶部