Commit 9238f36a authored by Johan Hedberg's avatar Johan Hedberg Committed by Gustavo Padovan

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: default avatarJohan Hedberg <johan.hedberg@intel.com>
Acked-by: default avatarMarcel Holtmann <marcel@holtmann.org>
Signed-off-by: default avatarGustavo Padovan <gustavo.padovan@collabora.co.uk>
parent 11714b3d
...@@ -1049,6 +1049,8 @@ struct hci_request { ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment