Bluetooth: Add request cmd_complete and cmd_status functions
This patch introduces functions to process the HCI request state when receiving HCI Command Status or Command Complete events. Some HCI commands, like Inquiry do not result in a Command complete event so special handling is needed for them. Inquiry is a particularly important one since it is the only forseeable "non-cmd_complete" command that will make good use of the request functionality, and its completion is either indicated by an Inquiry Complete event of a successful Command Complete for HCI_Inquiry_Cancel. Signed-off-by: Johan Hedberg <johan.hedberg@intel.com> Acked-by: Marcel Holtmann <marcel@holtmann.org> Signed-off-by: Gustavo Padovan <gustavo.padovan@collabora.co.uk>
This commit is contained in:
parent
11714b3d7a
commit
9238f36a5a
@ -1049,6 +1049,8 @@ struct hci_request {
|
|||||||
void hci_req_init(struct hci_request *req, struct hci_dev *hdev);
|
void hci_req_init(struct hci_request *req, struct hci_dev *hdev);
|
||||||
int hci_req_run(struct hci_request *req, hci_req_complete_t complete);
|
int hci_req_run(struct hci_request *req, hci_req_complete_t complete);
|
||||||
int hci_req_add(struct hci_request *req, u16 opcode, u32 plen, void *param);
|
int hci_req_add(struct hci_request *req, u16 opcode, u32 plen, void *param);
|
||||||
|
void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status);
|
||||||
|
void hci_req_cmd_status(struct hci_dev *hdev, u16 opcode, u8 status);
|
||||||
|
|
||||||
int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param);
|
int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param);
|
||||||
void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags);
|
void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags);
|
||||||
|
@ -3208,6 +3208,91 @@ static void hci_scodata_packet(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool hci_req_is_complete(struct hci_dev *hdev)
|
||||||
|
{
|
||||||
|
struct sk_buff *skb;
|
||||||
|
|
||||||
|
skb = skb_peek(&hdev->cmd_q);
|
||||||
|
if (!skb)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return bt_cb(skb)->req.start;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status)
|
||||||
|
{
|
||||||
|
hci_req_complete_t req_complete = NULL;
|
||||||
|
struct sk_buff *skb;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
BT_DBG("opcode 0x%04x status 0x%02x", opcode, status);
|
||||||
|
|
||||||
|
/* Check that the completed command really matches the last one
|
||||||
|
* that was sent.
|
||||||
|
*/
|
||||||
|
if (!hci_sent_cmd_data(hdev, opcode))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* If the command succeeded and there's still more commands in
|
||||||
|
* this request the request is not yet complete.
|
||||||
|
*/
|
||||||
|
if (!status && !hci_req_is_complete(hdev))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* If this was the last command in a request the complete
|
||||||
|
* callback would be found in hdev->sent_cmd instead of the
|
||||||
|
* command queue (hdev->cmd_q).
|
||||||
|
*/
|
||||||
|
if (hdev->sent_cmd) {
|
||||||
|
req_complete = bt_cb(hdev->sent_cmd)->req.complete;
|
||||||
|
if (req_complete)
|
||||||
|
goto call_complete;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove all pending commands belonging to this request */
|
||||||
|
spin_lock_irqsave(&hdev->cmd_q.lock, flags);
|
||||||
|
while ((skb = __skb_dequeue(&hdev->cmd_q))) {
|
||||||
|
if (bt_cb(skb)->req.start) {
|
||||||
|
__skb_queue_head(&hdev->cmd_q, skb);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
req_complete = bt_cb(skb)->req.complete;
|
||||||
|
kfree_skb(skb);
|
||||||
|
}
|
||||||
|
spin_unlock_irqrestore(&hdev->cmd_q.lock, flags);
|
||||||
|
|
||||||
|
call_complete:
|
||||||
|
if (req_complete)
|
||||||
|
req_complete(hdev, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hci_req_cmd_status(struct hci_dev *hdev, u16 opcode, u8 status)
|
||||||
|
{
|
||||||
|
hci_req_complete_t req_complete = NULL;
|
||||||
|
|
||||||
|
BT_DBG("opcode 0x%04x status 0x%02x", opcode, status);
|
||||||
|
|
||||||
|
if (status) {
|
||||||
|
hci_req_cmd_complete(hdev, opcode, status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No need to handle success status if there are more commands */
|
||||||
|
if (!hci_req_is_complete(hdev))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (hdev->sent_cmd)
|
||||||
|
req_complete = bt_cb(hdev->sent_cmd)->req.complete;
|
||||||
|
|
||||||
|
/* If the request doesn't have a complete callback or there
|
||||||
|
* are other commands/requests in the hdev queue we consider
|
||||||
|
* this request as completed.
|
||||||
|
*/
|
||||||
|
if (!req_complete || !skb_queue_empty(&hdev->cmd_q))
|
||||||
|
hci_req_cmd_complete(hdev, opcode, status);
|
||||||
|
}
|
||||||
|
|
||||||
static void hci_rx_work(struct work_struct *work)
|
static void hci_rx_work(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct hci_dev *hdev = container_of(work, struct hci_dev, rx_work);
|
struct hci_dev *hdev = container_of(work, struct hci_dev, rx_work);
|
||||||
|
@ -53,6 +53,7 @@ static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
|
hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
|
||||||
hci_dev_unlock(hdev);
|
hci_dev_unlock(hdev);
|
||||||
|
|
||||||
|
hci_req_cmd_complete(hdev, HCI_OP_INQUIRY, status);
|
||||||
hci_req_complete(hdev, HCI_OP_INQUIRY_CANCEL, status);
|
hci_req_complete(hdev, HCI_OP_INQUIRY_CANCEL, status);
|
||||||
|
|
||||||
hci_conn_check_pending(hdev);
|
hci_conn_check_pending(hdev);
|
||||||
@ -1692,6 +1693,7 @@ static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
|
|
||||||
BT_DBG("%s status 0x%2.2x", hdev->name, status);
|
BT_DBG("%s status 0x%2.2x", hdev->name, status);
|
||||||
|
|
||||||
|
hci_req_cmd_complete(hdev, HCI_OP_INQUIRY, status);
|
||||||
hci_req_complete(hdev, HCI_OP_INQUIRY, status);
|
hci_req_complete(hdev, HCI_OP_INQUIRY, status);
|
||||||
|
|
||||||
hci_conn_check_pending(hdev);
|
hci_conn_check_pending(hdev);
|
||||||
@ -2254,6 +2256,7 @@ static void hci_qos_setup_complete_evt(struct hci_dev *hdev,
|
|||||||
static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
struct hci_ev_cmd_complete *ev = (void *) skb->data;
|
struct hci_ev_cmd_complete *ev = (void *) skb->data;
|
||||||
|
u8 status = skb->data[sizeof(*ev)];
|
||||||
__u16 opcode;
|
__u16 opcode;
|
||||||
|
|
||||||
skb_pull(skb, sizeof(*ev));
|
skb_pull(skb, sizeof(*ev));
|
||||||
@ -2497,6 +2500,8 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
if (ev->opcode != HCI_OP_NOP)
|
if (ev->opcode != HCI_OP_NOP)
|
||||||
del_timer(&hdev->cmd_timer);
|
del_timer(&hdev->cmd_timer);
|
||||||
|
|
||||||
|
hci_req_cmd_complete(hdev, ev->opcode, status);
|
||||||
|
|
||||||
if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags)) {
|
if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags)) {
|
||||||
atomic_set(&hdev->cmd_cnt, 1);
|
atomic_set(&hdev->cmd_cnt, 1);
|
||||||
if (!skb_queue_empty(&hdev->cmd_q))
|
if (!skb_queue_empty(&hdev->cmd_q))
|
||||||
@ -2590,6 +2595,8 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
if (ev->opcode != HCI_OP_NOP)
|
if (ev->opcode != HCI_OP_NOP)
|
||||||
del_timer(&hdev->cmd_timer);
|
del_timer(&hdev->cmd_timer);
|
||||||
|
|
||||||
|
hci_req_cmd_status(hdev, ev->opcode, ev->status);
|
||||||
|
|
||||||
if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags)) {
|
if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags)) {
|
||||||
atomic_set(&hdev->cmd_cnt, 1);
|
atomic_set(&hdev->cmd_cnt, 1);
|
||||||
if (!skb_queue_empty(&hdev->cmd_q))
|
if (!skb_queue_empty(&hdev->cmd_q))
|
||||||
|
Loading…
Reference in New Issue
Block a user