diff options
author | Alan Ott <alan@signal11.us> | 2011-01-18 03:04:38 -0500 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2011-02-11 09:05:49 -0500 |
commit | 0825411ade21a39ac63b3e011d092b1f95b5f3f5 (patch) | |
tree | c69a0088e46c93ef1a0916030af3e02c4cce04e9 /net/bluetooth | |
parent | 0f69dca20f77dc374b67e17e10b30cec37e778c4 (diff) |
HID: bt: Wait for ACK on Sent Reports
Wait for an ACK from the device before returning from
hidp_output_raw_report(). This way, failures can be returned to the user
application. Also, it prevents ACK/NAK packets from an output packet from
being confused with ACK/NAK packets from an input request packet.
Signed-off-by: Alan Ott <alan@signal11.us>
Acked-by: Gustavo F. Padovan <padovan@profusion.mobi>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'net/bluetooth')
-rw-r--r-- | net/bluetooth/hidp/core.c | 54 | ||||
-rw-r--r-- | net/bluetooth/hidp/hidp.h | 4 |
2 files changed, 55 insertions, 3 deletions
diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 67cc4bc82c68..5383e6c7d09d 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c | |||
@@ -316,6 +316,9 @@ static int hidp_send_report(struct hidp_session *session, struct hid_report *rep | |||
316 | static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, size_t count, | 316 | static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, size_t count, |
317 | unsigned char report_type) | 317 | unsigned char report_type) |
318 | { | 318 | { |
319 | struct hidp_session *session = hid->driver_data; | ||
320 | int ret; | ||
321 | |||
319 | switch (report_type) { | 322 | switch (report_type) { |
320 | case HID_FEATURE_REPORT: | 323 | case HID_FEATURE_REPORT: |
321 | report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE; | 324 | report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE; |
@@ -327,10 +330,47 @@ static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, s | |||
327 | return -EINVAL; | 330 | return -EINVAL; |
328 | } | 331 | } |
329 | 332 | ||
333 | if (mutex_lock_interruptible(&session->report_mutex)) | ||
334 | return -ERESTARTSYS; | ||
335 | |||
336 | /* Set up our wait, and send the report request to the device. */ | ||
337 | set_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags); | ||
330 | if (hidp_send_ctrl_message(hid->driver_data, report_type, | 338 | if (hidp_send_ctrl_message(hid->driver_data, report_type, |
331 | data, count)) | 339 | data, count)) { |
332 | return -ENOMEM; | 340 | ret = -ENOMEM; |
333 | return count; | 341 | goto err; |
342 | } | ||
343 | |||
344 | /* Wait for the ACK from the device. */ | ||
345 | while (test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags)) { | ||
346 | int res; | ||
347 | |||
348 | res = wait_event_interruptible_timeout(session->report_queue, | ||
349 | !test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags), | ||
350 | 10*HZ); | ||
351 | if (res == 0) { | ||
352 | /* timeout */ | ||
353 | ret = -EIO; | ||
354 | goto err; | ||
355 | } | ||
356 | if (res < 0) { | ||
357 | /* signal */ | ||
358 | ret = -ERESTARTSYS; | ||
359 | goto err; | ||
360 | } | ||
361 | } | ||
362 | |||
363 | if (!session->output_report_success) { | ||
364 | ret = -EIO; | ||
365 | goto err; | ||
366 | } | ||
367 | |||
368 | ret = count; | ||
369 | |||
370 | err: | ||
371 | clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags); | ||
372 | mutex_unlock(&session->report_mutex); | ||
373 | return ret; | ||
334 | } | 374 | } |
335 | 375 | ||
336 | static void hidp_idle_timeout(unsigned long arg) | 376 | static void hidp_idle_timeout(unsigned long arg) |
@@ -357,10 +397,12 @@ static void hidp_process_handshake(struct hidp_session *session, | |||
357 | unsigned char param) | 397 | unsigned char param) |
358 | { | 398 | { |
359 | BT_DBG("session %p param 0x%02x", session, param); | 399 | BT_DBG("session %p param 0x%02x", session, param); |
400 | session->output_report_success = 0; /* default condition */ | ||
360 | 401 | ||
361 | switch (param) { | 402 | switch (param) { |
362 | case HIDP_HSHK_SUCCESSFUL: | 403 | case HIDP_HSHK_SUCCESSFUL: |
363 | /* FIXME: Call into SET_ GET_ handlers here */ | 404 | /* FIXME: Call into SET_ GET_ handlers here */ |
405 | session->output_report_success = 1; | ||
364 | break; | 406 | break; |
365 | 407 | ||
366 | case HIDP_HSHK_NOT_READY: | 408 | case HIDP_HSHK_NOT_READY: |
@@ -385,6 +427,12 @@ static void hidp_process_handshake(struct hidp_session *session, | |||
385 | HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0); | 427 | HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0); |
386 | break; | 428 | break; |
387 | } | 429 | } |
430 | |||
431 | /* Wake up the waiting thread. */ | ||
432 | if (test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags)) { | ||
433 | clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags); | ||
434 | wake_up_interruptible(&session->report_queue); | ||
435 | } | ||
388 | } | 436 | } |
389 | 437 | ||
390 | static void hidp_process_hid_control(struct hidp_session *session, | 438 | static void hidp_process_hid_control(struct hidp_session *session, |
diff --git a/net/bluetooth/hidp/hidp.h b/net/bluetooth/hidp/hidp.h index 2cc35dc8fa03..92e093e61f27 100644 --- a/net/bluetooth/hidp/hidp.h +++ b/net/bluetooth/hidp/hidp.h | |||
@@ -80,6 +80,7 @@ | |||
80 | #define HIDP_VIRTUAL_CABLE_UNPLUG 0 | 80 | #define HIDP_VIRTUAL_CABLE_UNPLUG 0 |
81 | #define HIDP_BOOT_PROTOCOL_MODE 1 | 81 | #define HIDP_BOOT_PROTOCOL_MODE 1 |
82 | #define HIDP_BLUETOOTH_VENDOR_ID 9 | 82 | #define HIDP_BLUETOOTH_VENDOR_ID 9 |
83 | #define HIDP_WAITING_FOR_SEND_ACK 11 | ||
83 | 84 | ||
84 | struct hidp_connadd_req { | 85 | struct hidp_connadd_req { |
85 | int ctrl_sock; // Connected control socket | 86 | int ctrl_sock; // Connected control socket |
@@ -154,6 +155,9 @@ struct hidp_session { | |||
154 | struct sk_buff_head ctrl_transmit; | 155 | struct sk_buff_head ctrl_transmit; |
155 | struct sk_buff_head intr_transmit; | 156 | struct sk_buff_head intr_transmit; |
156 | 157 | ||
158 | /* Used in hidp_output_raw_report() */ | ||
159 | int output_report_success; /* boolean */ | ||
160 | |||
157 | /* Report descriptor */ | 161 | /* Report descriptor */ |
158 | __u8 *rd_data; | 162 | __u8 *rd_data; |
159 | uint rd_size; | 163 | uint rd_size; |