[RFC 5/5] bluetooth: Implement Remove Network Management API command

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Queue Remove Network Management API command and disconnect the L2CAP
channel. Once the channel has been removed, reply to the command in
l2cap_ops close callback. On error reply with a failure in the
l2cap_ops status callback.

Remove the remaining part of the debugfs code handling the control
file.

The MGMT_OP_REMOVE_NETWORK command is defined as:

       Command Code:           0x0044
       Controller Index:       <controller id>
       Command Parameters:     Address (6 Octets)
                               Address_Type (1 Octet)
       Return Parameters:      Address (6 Octets)
                               Address_Type (1 Octet)

       This command is used to disconnect a network connection from a
       remote BTLE node. A pre-requisite is that LE is already
       enabled, otherwise this command will return a "rejected"
       response.

       Possible values for the Address_Type parameter:
               0       Reserved
               1       LE Public
               2       LE Random

       This command generates a Command Complete event on success
       or failure.

       Possible errors:        Busy
                               Not Connected
                               Invalid Parameters
                               Not Powered
                               Invalid Index

The MGMT_EV_NETWORK_REMOVED event is defined as:

       Event Code:             0x0026
       Controller Index:       <controller id>
       Event Parameters:       Address (6 Octets)
                               Address_Type (1 Octet)

       This event indicates that the BTLE network connection to a remote
       node has been lost.

       Possible values for the Address_Type parameter:
               1       LE Public
               2       LE Random

       For devices using resolvable random addresses with a known
       identity resolving key, the Address and Address_Type will
       contain the identity information.
---
 include/net/bluetooth/mgmt.h |  14 ++++
 net/bluetooth/6lowpan.c      | 174 ++++++++++++++++++++++---------------------
 net/bluetooth/mgmt.c         |   3 +
 3 files changed, 106 insertions(+), 85 deletions(-)

diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
index f6bc80f..62d0583 100644
--- a/include/net/bluetooth/mgmt.h
+++ b/include/net/bluetooth/mgmt.h
@@ -606,6 +606,15 @@ struct mgmt_rp_add_network {
 	int ifindex;
 } __packed;
 
+#define MGMT_OP_REMOVE_NETWORK          0x0044
+#define MGMT_REMOVE_NETWORK_SIZE        7
+struct mgmt_cp_remove_network {
+	struct mgmt_addr_info dst;
+} __packed;
+struct mgmt_rp_remove_network {
+	struct mgmt_addr_info dst;
+} __packed;
+
 #define MGMT_EV_CMD_COMPLETE		0x0001
 struct mgmt_ev_cmd_complete {
 	__le16	opcode;
@@ -825,3 +834,8 @@ struct mgmt_ev_network_added {
 	struct mgmt_addr_info dst;
 	int ifindex;
 } __packed;
+
+#define MGMT_EV_NETWORK_REMOVED         0x0026
+struct mgmt_ev_network_removed {
+	struct mgmt_addr_info dst;
+} __packed;
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
index 9f12eb1..f62ac14 100644
--- a/net/bluetooth/6lowpan.c
+++ b/net/bluetooth/6lowpan.c
@@ -33,7 +33,6 @@
 #define VERSION "0.1"
 
 static struct dentry *lowpan_enable_debugfs;
-static struct dentry *lowpan_control_debugfs;
 
 #define IFACE_NAME_TEMPLATE "bt%d"
 
@@ -929,6 +928,46 @@ static void cmd_add_network_complete(struct l2cap_chan *chan, int status,
 		mgmt_pending_remove(cmd);
 }
 
+static void cmd_remove_network_complete(struct l2cap_chan *chan, int status)
+{
+	struct hci_dev *hdev;
+	struct mgmt_pending_cmd *cmd;
+
+	hdev = hci_get_route(NULL, &chan->src);
+
+	if (!hdev) {
+		BT_DBG("No matching hci_dev for l2cap_chan %p", chan);
+		return;
+	}
+
+	cmd = mgmt_pending_find(HCI_CHANNEL_CONTROL, MGMT_OP_REMOVE_NETWORK,
+				hdev);
+	if (cmd) {
+		struct mgmt_cp_remove_network *cp = cmd->param;
+		struct mgmt_rp_remove_network rp;
+
+		bacpy(&rp.dst.bdaddr, &cp->dst.bdaddr);
+		rp.dst.type = cp->dst.type;
+
+		mgmt_cmd_complete(cmd->sk, cmd->index, cmd->opcode,
+				status, &rp, sizeof(rp));
+	}
+
+	if (status == MGMT_STATUS_SUCCESS && chan->state == BT_DISCONN) {
+		struct mgmt_ev_network_removed ep;
+
+		bacpy(&ep.dst.bdaddr, &chan->dst);
+		ep.dst.type = chan->dst_type;
+
+		mgmt_send_event(MGMT_EV_NETWORK_REMOVED, hdev,
+				HCI_CHANNEL_CONTROL, &ep, sizeof(ep),
+				HCI_SOCK_TRUSTED, cmd? cmd->sk: NULL);
+	}
+
+	if (cmd)
+		mgmt_pending_remove(cmd);
+}
+
 static inline void chan_ready_cb(struct l2cap_chan *chan)
 {
 	struct lowpan_dev *dev;
@@ -1000,6 +1039,7 @@ static void chan_close_cb(struct l2cap_chan *chan)
 	}
 
 	cmd_add_network_complete(chan, MGMT_STATUS_CONNECT_FAILED, 0);
+	cmd_remove_network_complete(chan, MGMT_STATUS_SUCCESS);
 
 	spin_lock(&devices_lock);
 
@@ -1045,6 +1085,7 @@ static void chan_state_change_cb(struct l2cap_chan *chan, int state, int err)
 
 	if (err < 0) {
 		cmd_add_network_complete(chan, MGMT_STATUS_CONNECT_FAILED, 0);
+		cmd_remove_network_complete(chan, MGMT_STATUS_CONNECT_FAILED);
 	}
 }
 
@@ -1270,21 +1311,60 @@ unlock:
 	return err;
 }
 
-static int bt_6lowpan_disconnect(struct l2cap_conn *conn, u8 dst_type)
+int bt_6lowpan_remove_network(struct sock *sk, struct hci_dev *hdev,
+			void *data, u16 data_len)
 {
+	struct mgmt_cp_remove_network *cp = data;
+	int err = 0;
 	struct lowpan_peer *peer;
+	struct mgmt_pending_cmd *cmd;
 
-	BT_DBG("conn %p dst type %d", conn, dst_type);
+	BT_DBG("remove network hdev %p data %p data len %d",
+		hdev, data, data_len);
 
-	peer = lookup_peer(conn);
-	if (!peer)
-		return -ENOENT;
+	if (!lmp_le_capable(hdev)) {
+		err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_NETWORK,
+					MGMT_STATUS_NOT_SUPPORTED, cp,
+					sizeof(*cp));
+		goto failed;
+	}
 
-	BT_DBG("peer %p chan %p", peer, peer->chan);
+	if (!bdaddr_type_is_le(cp->dst.type)) {
+		err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_NETWORK,
+					MGMT_STATUS_INVALID_PARAMS, cp,
+					sizeof(*cp));
+		goto failed;
+	}
 
-	l2cap_chan_close(peer->chan, ENOENT);
+	hci_dev_lock(hdev);
 
-	return 0;
+	if (mgmt_pending_find(HCI_CHANNEL_CONTROL, MGMT_OP_REMOVE_NETWORK,
+				hdev)) {
+		err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_REMOVE_NETWORK,
+				MGMT_STATUS_BUSY);
+		goto unlock;
+	}
+
+	peer = lookup_bdaddr(hdev, &cp->dst.bdaddr, cp->dst.type);
+	if (!peer) {
+		err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_NETWORK,
+					MGMT_STATUS_FAILED,
+					cp, sizeof(*cp));
+		goto unlock;
+	}
+
+	cmd = mgmt_pending_add(sk, MGMT_OP_REMOVE_NETWORK, hdev,
+			data, data_len);
+	if (!cmd)
+		err = -ENOMEM;
+
+	l2cap_chan_close(peer->chan, 0);
+
+unlock:
+	hci_dev_unlock(hdev);
+
+failed:
+	return err;
 }
 
 static struct l2cap_chan *bt_6lowpan_listen(void)
@@ -1318,40 +1398,6 @@ static struct l2cap_chan *bt_6lowpan_listen(void)
 	return chan;
 }
 
-static int get_l2cap_conn(char *buf, bdaddr_t *addr, u8 *addr_type,
-			  struct l2cap_conn **conn)
-{
-	struct hci_conn *hcon;
-	struct hci_dev *hdev;
-	bdaddr_t *src = BDADDR_ANY;
-	int n;
-
-	n = sscanf(buf, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx %hhu",
-		   &addr->b[5], &addr->b[4], &addr->b[3],
-		   &addr->b[2], &addr->b[1], &addr->b[0],
-		   addr_type);
-
-	if (n < 7)
-		return -EINVAL;
-
-	hdev = hci_get_route(addr, src);
-	if (!hdev)
-		return -ENOENT;
-
-	hci_dev_lock(hdev);
-	hcon = hci_conn_hash_lookup_le(hdev, addr, *addr_type);
-	hci_dev_unlock(hdev);
-
-	if (!hcon)
-		return -ENOENT;
-
-	*conn = (struct l2cap_conn *)hcon->l2cap_data;
-
-	BT_DBG("conn %p dst %pMR type %d", *conn, &hcon->dst, hcon->dst_type);
-
-	return 0;
-}
-
 static void disconnect_all_peers(void)
 {
 	struct lowpan_dev *entry;
@@ -1445,44 +1491,6 @@ static int lowpan_enable_get(void *data, u64 *val)
 DEFINE_SIMPLE_ATTRIBUTE(lowpan_enable_fops, lowpan_enable_get,
 			lowpan_enable_set, "%llu\n");
 
-static ssize_t lowpan_control_write(struct file *fp,
-				    const char __user *user_buffer,
-				    size_t count,
-				    loff_t *position)
-{
-	char buf[32];
-	size_t buf_size = min(count, sizeof(buf) - 1);
-	int ret;
-	bdaddr_t addr;
-	u8 addr_type;
-	struct l2cap_conn *conn = NULL;
-
-	if (copy_from_user(buf, user_buffer, buf_size))
-		return -EFAULT;
-
-	buf[buf_size] = '\0';
-
-	if (memcmp(buf, "disconnect ", 11) == 0) {
-		ret = get_l2cap_conn(&buf[11], &addr, &addr_type, &conn);
-		if (ret < 0)
-			return ret;
-
-		ret = bt_6lowpan_disconnect(conn, addr_type);
-		if (ret < 0)
-			return ret;
-
-		return count;
-	}
-
-	return count;
-}
-
-static const struct file_operations lowpan_control_fops = {
-	.write		= lowpan_control_write,
-	.llseek		= seq_lseek,
-	.release	= single_release,
-};
-
 static void disconnect_devices(void)
 {
 	struct lowpan_dev *entry, *tmp, *new_dev;
@@ -1555,9 +1563,6 @@ int bt_6lowpan_init(void)
 	lowpan_enable_debugfs = debugfs_create_file("6lowpan_enable", 0644,
 						    bt_debugfs, NULL,
 						    &lowpan_enable_fops);
-	lowpan_control_debugfs = debugfs_create_file("6lowpan_control", 0644,
-						     bt_debugfs, NULL,
-						     &lowpan_control_fops);
 
 	return register_netdevice_notifier(&bt_6lowpan_dev_notifier);
 }
@@ -1565,7 +1570,6 @@ int bt_6lowpan_init(void)
 void bt_6lowpan_exit(void)
 {
 	debugfs_remove(lowpan_enable_debugfs);
-	debugfs_remove(lowpan_control_debugfs);
 
 	if (listen_chan) {
 		l2cap_chan_close(listen_chan, 0);
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 2c2824e..909c723 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -107,6 +107,7 @@ static const u16 mgmt_commands[] = {
 	MGMT_OP_START_LIMITED_DISCOVERY,
 	MGMT_OP_GET_NETWORKS,
 	MGMT_OP_ADD_NETWORK,
+	MGMT_OP_REMOVE_NETWORK,
 };
 
 static const u16 mgmt_events[] = {
@@ -145,6 +146,7 @@ static const u16 mgmt_events[] = {
 	MGMT_EV_ADVERTISING_ADDED,
 	MGMT_EV_ADVERTISING_REMOVED,
 	MGMT_EV_NETWORK_ADDED,
+	MGMT_EV_NETWORK_REMOVED,
 };
 
 static const u16 mgmt_untrusted_commands[] = {
@@ -6342,6 +6344,7 @@ static const struct hci_mgmt_handler mgmt_handlers[] = {
 	{ start_limited_discovery, MGMT_START_DISCOVERY_SIZE },
 	{ bt_6lowpan_get_networks, MGMT_GET_NETWORKS_SIZE },
 	{ bt_6lowpan_add_network,  MGMT_ADD_NETWORK_SIZE },
+	{ bt_6lowpan_remove_network, MGMT_REMOVE_NETWORK_SIZE },
 };
 
 void mgmt_index_added(struct hci_dev *hdev)
-- 
2.1.4

--
To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux