aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorJohan Hedberg <johan.hedberg@intel.com>2013-04-02 06:35:04 -0400
committerJohan Hedberg <johan.hedberg@intel.com>2013-04-04 12:16:06 -0400
commit75e84b7c522c6e07964cd1f5bf28535768a1e9fa (patch)
treef446f05dddbc3fff7f9345ac524239f4e7d19baf /net
parentb6ddb638235d90ed67af9af40e63880fd66a1939 (diff)
Bluetooth: Add __hci_cmd_sync() helper function
This patch adds a helper function for sending a single HCI command waiting for its completion and then returning back the parameters in the resulting command complete event (if there was one). The implementation is very similar to that of hci_req_sync() except that instead of invocing a callback for sending HCI commands the function constructs and sends one itself and after being woken up picks the last received event from hdev->recv_evt (if it matches the right criteria) and returns it. Signed-off-by: Johan Hedberg <johan.hedberg@intel.com> Acked-by: Marcel Holtmann <marcel@holtmann.org>
Diffstat (limited to 'net')
-rw-r--r--net/bluetooth/hci_core.c102
1 files changed, 102 insertions, 0 deletions
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 7c323bd112ff..8b2d543fb143 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -79,6 +79,108 @@ static void hci_req_cancel(struct hci_dev *hdev, int err)
79 } 79 }
80} 80}
81 81
82struct sk_buff *hci_get_cmd_complete(struct hci_dev *hdev, u16 opcode)
83{
84 struct hci_ev_cmd_complete *ev;
85 struct hci_event_hdr *hdr;
86 struct sk_buff *skb;
87
88 hci_dev_lock(hdev);
89
90 skb = hdev->recv_evt;
91 hdev->recv_evt = NULL;
92
93 hci_dev_unlock(hdev);
94
95 if (!skb)
96 return ERR_PTR(-ENODATA);
97
98 if (skb->len < sizeof(*hdr)) {
99 BT_ERR("Too short HCI event");
100 goto failed;
101 }
102
103 hdr = (void *) skb->data;
104 skb_pull(skb, HCI_EVENT_HDR_SIZE);
105
106 if (hdr->evt != HCI_EV_CMD_COMPLETE) {
107 BT_DBG("Last event is not cmd complete (0x%2.2x)", hdr->evt);
108 goto failed;
109 }
110
111 if (skb->len < sizeof(*ev)) {
112 BT_ERR("Too short cmd_complete event");
113 goto failed;
114 }
115
116 ev = (void *) skb->data;
117 skb_pull(skb, sizeof(*ev));
118
119 if (opcode == __le16_to_cpu(ev->opcode))
120 return skb;
121
122 BT_DBG("opcode doesn't match (0x%2.2x != 0x%2.2x)", opcode,
123 __le16_to_cpu(ev->opcode));
124
125failed:
126 kfree_skb(skb);
127 return ERR_PTR(-ENODATA);
128}
129
130struct sk_buff *__hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen,
131 void *param, u32 timeout)
132{
133 DECLARE_WAITQUEUE(wait, current);
134 struct hci_request req;
135 int err = 0;
136
137 BT_DBG("%s", hdev->name);
138
139 hci_req_init(&req, hdev);
140
141 hci_req_add(&req, opcode, plen, param);
142
143 hdev->req_status = HCI_REQ_PEND;
144
145 err = hci_req_run(&req, hci_req_sync_complete);
146 if (err < 0)
147 return ERR_PTR(err);
148
149 add_wait_queue(&hdev->req_wait_q, &wait);
150 set_current_state(TASK_INTERRUPTIBLE);
151
152 schedule_timeout(timeout);
153
154 remove_wait_queue(&hdev->req_wait_q, &wait);
155
156 if (signal_pending(current))
157 return ERR_PTR(-EINTR);
158
159 switch (hdev->req_status) {
160 case HCI_REQ_DONE:
161 err = -bt_to_errno(hdev->req_result);
162 break;
163
164 case HCI_REQ_CANCELED:
165 err = -hdev->req_result;
166 break;
167
168 default:
169 err = -ETIMEDOUT;
170 break;
171 }
172
173 hdev->req_status = hdev->req_result = 0;
174
175 BT_DBG("%s end: err %d", hdev->name, err);
176
177 if (err < 0)
178 return ERR_PTR(err);
179
180 return hci_get_cmd_complete(hdev, opcode);
181}
182EXPORT_SYMBOL(__hci_cmd_sync);
183
82/* Execute request and wait for completion. */ 184/* Execute request and wait for completion. */
83static int __hci_req_sync(struct hci_dev *hdev, 185static int __hci_req_sync(struct hci_dev *hdev,
84 void (*func)(struct hci_request *req, 186 void (*func)(struct hci_request *req,