mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	Bluetooth: MGMT: Add initial implementation of MGMT_OP_HCI_CMD_SYNC
This adds the initial implementation of MGMT_OP_HCI_CMD_SYNC as documented in mgmt-api (BlueZ tree): Send HCI command and wait for event Command =========================================== Command Code: 0x005B Controller Index: <controller id> Command Parameters: Opcode (2 Octets) Event (1 Octet) Timeout (1 Octet) Parameter Length (2 Octets) Parameter (variable) Return Parameters: Response (1-variable Octets) This command may be used to send a HCI command and wait for an (optional) event. The HCI command is specified by the Opcode, any arbitrary is supported including vendor commands, but contrary to the like of Raw/User channel it is run as an HCI command send by the kernel since it uses its command synchronization thus it is possible to wait for a specific event as a response. Setting event to 0x00 will cause the command to wait for either HCI Command Status or HCI Command Complete. Timeout is specified in seconds, setting it to 0 will cause the default timeout to be used. Possible errors: Failed Invalid Parameters Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This commit is contained in:
		
							parent
							
								
									27aabf27fd
								
							
						
					
					
						commit
						827af4787e
					
				
					 2 changed files with 70 additions and 0 deletions
				
			
		| 
						 | 
					@ -878,6 +878,16 @@ struct mgmt_cp_mesh_send_cancel {
 | 
				
			||||||
} __packed;
 | 
					} __packed;
 | 
				
			||||||
#define MGMT_MESH_SEND_CANCEL_SIZE	1
 | 
					#define MGMT_MESH_SEND_CANCEL_SIZE	1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MGMT_OP_HCI_CMD_SYNC		0x005B
 | 
				
			||||||
 | 
					struct mgmt_cp_hci_cmd_sync {
 | 
				
			||||||
 | 
						__le16 opcode;
 | 
				
			||||||
 | 
						__u8   event;
 | 
				
			||||||
 | 
						__u8   timeout;
 | 
				
			||||||
 | 
						__le16 params_len;
 | 
				
			||||||
 | 
						__u8   params[];
 | 
				
			||||||
 | 
					} __packed;
 | 
				
			||||||
 | 
					#define MGMT_HCI_CMD_SYNC_SIZE		6
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define MGMT_EV_CMD_COMPLETE		0x0001
 | 
					#define MGMT_EV_CMD_COMPLETE		0x0001
 | 
				
			||||||
struct mgmt_ev_cmd_complete {
 | 
					struct mgmt_ev_cmd_complete {
 | 
				
			||||||
	__le16	opcode;
 | 
						__le16	opcode;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -132,6 +132,7 @@ static const u16 mgmt_commands[] = {
 | 
				
			||||||
	MGMT_OP_MESH_READ_FEATURES,
 | 
						MGMT_OP_MESH_READ_FEATURES,
 | 
				
			||||||
	MGMT_OP_MESH_SEND,
 | 
						MGMT_OP_MESH_SEND,
 | 
				
			||||||
	MGMT_OP_MESH_SEND_CANCEL,
 | 
						MGMT_OP_MESH_SEND_CANCEL,
 | 
				
			||||||
 | 
						MGMT_OP_HCI_CMD_SYNC,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const u16 mgmt_events[] = {
 | 
					static const u16 mgmt_events[] = {
 | 
				
			||||||
| 
						 | 
					@ -2515,6 +2516,64 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
 | 
				
			||||||
	return err;
 | 
						return err;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int send_hci_cmd_sync(struct hci_dev *hdev, void *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct mgmt_pending_cmd *cmd = data;
 | 
				
			||||||
 | 
						struct mgmt_cp_hci_cmd_sync *cp = cmd->param;
 | 
				
			||||||
 | 
						struct sk_buff *skb;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						skb = __hci_cmd_sync_ev(hdev, le16_to_cpu(cp->opcode),
 | 
				
			||||||
 | 
									le16_to_cpu(cp->params_len), cp->params,
 | 
				
			||||||
 | 
									cp->event, cp->timeout ?
 | 
				
			||||||
 | 
									msecs_to_jiffies(cp->timeout * 1000) :
 | 
				
			||||||
 | 
									HCI_CMD_TIMEOUT);
 | 
				
			||||||
 | 
						if (IS_ERR(skb)) {
 | 
				
			||||||
 | 
							mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_HCI_CMD_SYNC,
 | 
				
			||||||
 | 
									mgmt_status(PTR_ERR(skb)));
 | 
				
			||||||
 | 
							goto done;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_HCI_CMD_SYNC, 0,
 | 
				
			||||||
 | 
								  skb->data, skb->len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						kfree_skb(skb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					done:
 | 
				
			||||||
 | 
						mgmt_pending_free(cmd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int mgmt_hci_cmd_sync(struct sock *sk, struct hci_dev *hdev,
 | 
				
			||||||
 | 
								     void *data, u16 len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct mgmt_cp_hci_cmd_sync *cp = data;
 | 
				
			||||||
 | 
						struct mgmt_pending_cmd *cmd;
 | 
				
			||||||
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (len < sizeof(*cp))
 | 
				
			||||||
 | 
							return mgmt_cmd_status(sk, hdev->id, MGMT_OP_HCI_CMD_SYNC,
 | 
				
			||||||
 | 
									       MGMT_STATUS_INVALID_PARAMS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						hci_dev_lock(hdev);
 | 
				
			||||||
 | 
						cmd = mgmt_pending_new(sk, MGMT_OP_HCI_CMD_SYNC, hdev, data, len);
 | 
				
			||||||
 | 
						if (!cmd)
 | 
				
			||||||
 | 
							err = -ENOMEM;
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							err = hci_cmd_sync_queue(hdev, send_hci_cmd_sync, cmd, NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (err < 0) {
 | 
				
			||||||
 | 
							err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_HCI_CMD_SYNC,
 | 
				
			||||||
 | 
									      MGMT_STATUS_FAILED);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (cmd)
 | 
				
			||||||
 | 
								mgmt_pending_free(cmd);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						hci_dev_unlock(hdev);
 | 
				
			||||||
 | 
						return err;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* This is a helper function to test for pending mgmt commands that can
 | 
					/* This is a helper function to test for pending mgmt commands that can
 | 
				
			||||||
 * cause CoD or EIR HCI commands. We can only allow one such pending
 | 
					 * cause CoD or EIR HCI commands. We can only allow one such pending
 | 
				
			||||||
 * mgmt command at a time since otherwise we cannot easily track what
 | 
					 * mgmt command at a time since otherwise we cannot easily track what
 | 
				
			||||||
| 
						 | 
					@ -9371,6 +9430,7 @@ static const struct hci_mgmt_handler mgmt_handlers[] = {
 | 
				
			||||||
	{ mesh_send,               MGMT_MESH_SEND_SIZE,
 | 
						{ mesh_send,               MGMT_MESH_SEND_SIZE,
 | 
				
			||||||
						HCI_MGMT_VAR_LEN },
 | 
											HCI_MGMT_VAR_LEN },
 | 
				
			||||||
	{ mesh_send_cancel,        MGMT_MESH_SEND_CANCEL_SIZE },
 | 
						{ mesh_send_cancel,        MGMT_MESH_SEND_CANCEL_SIZE },
 | 
				
			||||||
 | 
						{ mgmt_hci_cmd_sync,       MGMT_HCI_CMD_SYNC_SIZE, HCI_MGMT_VAR_LEN },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void mgmt_index_added(struct hci_dev *hdev)
 | 
					void mgmt_index_added(struct hci_dev *hdev)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue