diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
index d382679efd2b..dba431baaef8 100644
--- a/include/net/bluetooth/mgmt.h
+++ b/include/net/bluetooth/mgmt.h
@@ -878,6 +878,25 @@ struct mgmt_cp_mesh_send_cancel {
 } __packed;
 #define MGMT_MESH_SEND_CANCEL_SIZE	1
 
+#define MGMT_OP_GET_DEVICE_IO_CAPABILITY         0x005B
+struct mgmt_cp_get_device_io_capability {
+	struct mgmt_addr_info addr;
+} __packed;
+#define MGMT_GET_DEVICE_IO_CAPABILITY_SIZE       MGMT_ADDR_INFO_SIZE
+struct mgmt_rp_get_device_io_capability {
+	struct mgmt_addr_info addr;
+	__u8 flags;
+} __packed;
+
+#define MGMT_DEVICE_IO_CAP_FLAG_IO_DISPLAY_ONLY       BIT(0)
+#define MGMT_DEVICE_IO_CAP_FLAG_IO_DISPLAY_YESNO      BIT(1)
+#define MGMT_DEVICE_IO_CAP_FLAG_IO_KEYBOARD_ONLY      BIT(2)
+#define MGMT_DEVICE_IO_CAP_FLAG_IO_NO_INPUT_OUTPUT    BIT(3)
+#define MGMT_DEVICE_IO_CAP_FLAG_AT_NO_BONDING         BIT(4)
+#define MGMT_DEVICE_IO_CAP_FLAG_AT_DEDICATED_BONDING  BIT(5)
+#define MGMT_DEVICE_IO_CAP_FLAG_AT_GENERAL_BONDING    BIT(6)
+#define MGMT_DEVICE_IO_CAP_FLAG_AT_MITM               BIT(7)
+
 #define MGMT_EV_CMD_COMPLETE		0x0001
 struct mgmt_ev_cmd_complete {
 	__le16	opcode;
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 78ab562807d0..92b4317e60a1 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -28,6 +28,7 @@
 #include <asm/unaligned.h>
 
 #include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci.h>
 #include <net/bluetooth/hci_core.h>
 #include <net/bluetooth/hci_sock.h>
 #include <net/bluetooth/l2cap.h>
@@ -67,6 +68,7 @@ static const u16 mgmt_commands[] = {
 	MGMT_OP_PIN_CODE_REPLY,
 	MGMT_OP_PIN_CODE_NEG_REPLY,
 	MGMT_OP_SET_IO_CAPABILITY,
+	MGMT_OP_GET_DEVICE_IO_CAPABILITY,
 	MGMT_OP_PAIR_DEVICE,
 	MGMT_OP_CANCEL_PAIR_DEVICE,
 	MGMT_OP_UNPAIR_DEVICE,
@@ -3303,6 +3305,92 @@ static int set_io_capability(struct sock *sk, struct hci_dev *hdev, void *data,
 				 NULL, 0);
 }
 
+static int get_device_io_capability(struct sock *sk, struct hci_dev *hdev,
+				    void *data, u16 len)
+{
+	struct mgmt_cp_get_device_io_capability *cp = data;
+	struct mgmt_rp_get_device_io_capability rp;
+	struct hci_conn *conn;
+	int err = 0;
+
+	bt_dev_dbg(hdev, "sock %p", sk);
+
+	memset(&rp, 0, sizeof(rp));
+	bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
+	rp.addr.type = cp->addr.type;
+
+	if (!bdaddr_type_is_valid(cp->addr.type))
+		return mgmt_cmd_complete(sk, hdev->id,
+					 MGMT_OP_GET_DEVICE_IO_CAPABILITY,
+					 MGMT_STATUS_INVALID_PARAMS, &rp,
+					 sizeof(rp));
+	hci_dev_lock(hdev);
+
+	if (!hdev_is_powered(hdev)) {
+		err = mgmt_cmd_complete(sk, hdev->id,
+					MGMT_OP_GET_DEVICE_IO_CAPABILITY,
+					MGMT_STATUS_NOT_POWERED, &rp,
+					sizeof(rp));
+		goto unlock;
+	}
+
+	if (cp->addr.type == BDADDR_BREDR)
+		conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
+					       &cp->addr.bdaddr);
+	else
+		conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->addr.bdaddr);
+
+	if (!conn) {
+		err = mgmt_cmd_complete(sk, hdev->id,
+					MGMT_OP_GET_DEVICE_IO_CAPABILITY,
+					MGMT_STATUS_NOT_CONNECTED, &rp,
+					sizeof(rp));
+		goto unlock;
+	}
+
+	switch (conn->remote_cap) {
+	case HCI_IO_DISPLAY_ONLY:
+		rp.flags |= MGMT_DEVICE_IO_CAP_FLAG_IO_DISPLAY_ONLY;
+		break;
+	case HCI_IO_DISPLAY_YESNO:
+		rp.flags |= MGMT_DEVICE_IO_CAP_FLAG_IO_DISPLAY_YESNO;
+		break;
+	case HCI_IO_KEYBOARD_ONLY:
+		rp.flags |= MGMT_DEVICE_IO_CAP_FLAG_IO_KEYBOARD_ONLY;
+		break;
+	case HCI_IO_NO_INPUT_OUTPUT:
+		rp.flags |= MGMT_DEVICE_IO_CAP_FLAG_IO_NO_INPUT_OUTPUT;
+		break;
+	}
+
+	switch (conn->remote_auth) {
+	case HCI_AT_NO_BONDING_MITM:
+		rp.flags |= MGMT_DEVICE_IO_CAP_FLAG_AT_MITM;
+		fallthrough;
+	case HCI_AT_NO_BONDING:
+		rp.flags |= MGMT_DEVICE_IO_CAP_FLAG_AT_NO_BONDING;
+		break;
+	case HCI_AT_DEDICATED_BONDING_MITM:
+		rp.flags |= MGMT_DEVICE_IO_CAP_FLAG_AT_MITM;
+		fallthrough;
+	case HCI_AT_DEDICATED_BONDING:
+		rp.flags |= MGMT_DEVICE_IO_CAP_FLAG_AT_DEDICATED_BONDING;
+		break;
+	case HCI_AT_GENERAL_BONDING_MITM:
+		rp.flags |= MGMT_DEVICE_IO_CAP_FLAG_AT_MITM;
+		fallthrough;
+	case HCI_AT_GENERAL_BONDING:
+		rp.flags |= MGMT_DEVICE_IO_CAP_FLAG_AT_GENERAL_BONDING;
+		break;
+	}
+
+	err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_GET_DEVICE_IO_CAPABILITY,
+				MGMT_STATUS_SUCCESS, &rp, sizeof(rp));
+unlock:
+	hci_dev_unlock(hdev);
+	return err;
+}
+
 static struct mgmt_pending_cmd *find_pairing(struct hci_conn *conn)
 {
 	struct hci_dev *hdev = conn->hdev;
@@ -9217,6 +9305,7 @@ static const struct hci_mgmt_handler mgmt_handlers[] = {
 	{ pin_code_reply,          MGMT_PIN_CODE_REPLY_SIZE },
 	{ pin_code_neg_reply,      MGMT_PIN_CODE_NEG_REPLY_SIZE },
 	{ set_io_capability,       MGMT_SET_IO_CAPABILITY_SIZE },
+	{ get_device_io_capability,MGMT_GET_DEVICE_IO_CAPABILITY_SIZE },
 	{ pair_device,             MGMT_PAIR_DEVICE_SIZE },
 	{ cancel_pair_device,      MGMT_CANCEL_PAIR_DEVICE_SIZE },
 	{ unpair_device,           MGMT_UNPAIR_DEVICE_SIZE },
