diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/bluetooth/hidp/core.c | 197 | ||||
-rw-r--r-- | net/bluetooth/hidp/hidp.h | 15 |
2 files changed, 196 insertions, 16 deletions
diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 29544c21f4b5..3c036b0933c1 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c | |||
@@ -36,6 +36,7 @@ | |||
36 | #include <linux/file.h> | 36 | #include <linux/file.h> |
37 | #include <linux/init.h> | 37 | #include <linux/init.h> |
38 | #include <linux/wait.h> | 38 | #include <linux/wait.h> |
39 | #include <linux/mutex.h> | ||
39 | #include <net/sock.h> | 40 | #include <net/sock.h> |
40 | 41 | ||
41 | #include <linux/input.h> | 42 | #include <linux/input.h> |
@@ -313,24 +314,144 @@ static int hidp_send_report(struct hidp_session *session, struct hid_report *rep | |||
313 | return hidp_queue_report(session, buf, rsize); | 314 | return hidp_queue_report(session, buf, rsize); |
314 | } | 315 | } |
315 | 316 | ||
317 | static int hidp_get_raw_report(struct hid_device *hid, | ||
318 | unsigned char report_number, | ||
319 | unsigned char *data, size_t count, | ||
320 | unsigned char report_type) | ||
321 | { | ||
322 | struct hidp_session *session = hid->driver_data; | ||
323 | struct sk_buff *skb; | ||
324 | size_t len; | ||
325 | int numbered_reports = hid->report_enum[report_type].numbered; | ||
326 | |||
327 | switch (report_type) { | ||
328 | case HID_FEATURE_REPORT: | ||
329 | report_type = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_FEATURE; | ||
330 | break; | ||
331 | case HID_INPUT_REPORT: | ||
332 | report_type = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_INPUT; | ||
333 | break; | ||
334 | case HID_OUTPUT_REPORT: | ||
335 | report_type = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_OUPUT; | ||
336 | break; | ||
337 | default: | ||
338 | return -EINVAL; | ||
339 | } | ||
340 | |||
341 | if (mutex_lock_interruptible(&session->report_mutex)) | ||
342 | return -ERESTARTSYS; | ||
343 | |||
344 | /* Set up our wait, and send the report request to the device. */ | ||
345 | session->waiting_report_type = report_type & HIDP_DATA_RTYPE_MASK; | ||
346 | session->waiting_report_number = numbered_reports ? report_number : -1; | ||
347 | set_bit(HIDP_WAITING_FOR_RETURN, &session->flags); | ||
348 | data[0] = report_number; | ||
349 | if (hidp_send_ctrl_message(hid->driver_data, report_type, data, 1)) | ||
350 | goto err_eio; | ||
351 | |||
352 | /* Wait for the return of the report. The returned report | ||
353 | gets put in session->report_return. */ | ||
354 | while (test_bit(HIDP_WAITING_FOR_RETURN, &session->flags)) { | ||
355 | int res; | ||
356 | |||
357 | res = wait_event_interruptible_timeout(session->report_queue, | ||
358 | !test_bit(HIDP_WAITING_FOR_RETURN, &session->flags), | ||
359 | 5*HZ); | ||
360 | if (res == 0) { | ||
361 | /* timeout */ | ||
362 | goto err_eio; | ||
363 | } | ||
364 | if (res < 0) { | ||
365 | /* signal */ | ||
366 | goto err_restartsys; | ||
367 | } | ||
368 | } | ||
369 | |||
370 | skb = session->report_return; | ||
371 | if (skb) { | ||
372 | len = skb->len < count ? skb->len : count; | ||
373 | memcpy(data, skb->data, len); | ||
374 | |||
375 | kfree_skb(skb); | ||
376 | session->report_return = NULL; | ||
377 | } else { | ||
378 | /* Device returned a HANDSHAKE, indicating protocol error. */ | ||
379 | len = -EIO; | ||
380 | } | ||
381 | |||
382 | clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags); | ||
383 | mutex_unlock(&session->report_mutex); | ||
384 | |||
385 | return len; | ||
386 | |||
387 | err_restartsys: | ||
388 | clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags); | ||
389 | mutex_unlock(&session->report_mutex); | ||
390 | return -ERESTARTSYS; | ||
391 | err_eio: | ||
392 | clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags); | ||
393 | mutex_unlock(&session->report_mutex); | ||
394 | return -EIO; | ||
395 | } | ||
396 | |||
316 | static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, size_t count, | 397 | static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, size_t count, |
317 | unsigned char report_type) | 398 | unsigned char report_type) |
318 | { | 399 | { |
400 | struct hidp_session *session = hid->driver_data; | ||
401 | int ret; | ||
402 | |||
319 | switch (report_type) { | 403 | switch (report_type) { |
320 | case HID_FEATURE_REPORT: | 404 | case HID_FEATURE_REPORT: |
321 | report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE; | 405 | report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE; |
322 | break; | 406 | break; |
323 | case HID_OUTPUT_REPORT: | 407 | case HID_OUTPUT_REPORT: |
324 | report_type = HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT; | 408 | report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_OUPUT; |
325 | break; | 409 | break; |
326 | default: | 410 | default: |
327 | return -EINVAL; | 411 | return -EINVAL; |
328 | } | 412 | } |
329 | 413 | ||
414 | if (mutex_lock_interruptible(&session->report_mutex)) | ||
415 | return -ERESTARTSYS; | ||
416 | |||
417 | /* Set up our wait, and send the report request to the device. */ | ||
418 | set_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags); | ||
330 | if (hidp_send_ctrl_message(hid->driver_data, report_type, | 419 | if (hidp_send_ctrl_message(hid->driver_data, report_type, |
331 | data, count)) | 420 | data, count)) { |
332 | return -ENOMEM; | 421 | ret = -ENOMEM; |
333 | return count; | 422 | goto err; |
423 | } | ||
424 | |||
425 | /* Wait for the ACK from the device. */ | ||
426 | while (test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags)) { | ||
427 | int res; | ||
428 | |||
429 | res = wait_event_interruptible_timeout(session->report_queue, | ||
430 | !test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags), | ||
431 | 10*HZ); | ||
432 | if (res == 0) { | ||
433 | /* timeout */ | ||
434 | ret = -EIO; | ||
435 | goto err; | ||
436 | } | ||
437 | if (res < 0) { | ||
438 | /* signal */ | ||
439 | ret = -ERESTARTSYS; | ||
440 | goto err; | ||
441 | } | ||
442 | } | ||
443 | |||
444 | if (!session->output_report_success) { | ||
445 | ret = -EIO; | ||
446 | goto err; | ||
447 | } | ||
448 | |||
449 | ret = count; | ||
450 | |||
451 | err: | ||
452 | clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags); | ||
453 | mutex_unlock(&session->report_mutex); | ||
454 | return ret; | ||
334 | } | 455 | } |
335 | 456 | ||
336 | static void hidp_idle_timeout(unsigned long arg) | 457 | static void hidp_idle_timeout(unsigned long arg) |
@@ -357,16 +478,22 @@ static void hidp_process_handshake(struct hidp_session *session, | |||
357 | unsigned char param) | 478 | unsigned char param) |
358 | { | 479 | { |
359 | BT_DBG("session %p param 0x%02x", session, param); | 480 | BT_DBG("session %p param 0x%02x", session, param); |
481 | session->output_report_success = 0; /* default condition */ | ||
360 | 482 | ||
361 | switch (param) { | 483 | switch (param) { |
362 | case HIDP_HSHK_SUCCESSFUL: | 484 | case HIDP_HSHK_SUCCESSFUL: |
363 | /* FIXME: Call into SET_ GET_ handlers here */ | 485 | /* FIXME: Call into SET_ GET_ handlers here */ |
486 | session->output_report_success = 1; | ||
364 | break; | 487 | break; |
365 | 488 | ||
366 | case HIDP_HSHK_NOT_READY: | 489 | case HIDP_HSHK_NOT_READY: |
367 | case HIDP_HSHK_ERR_INVALID_REPORT_ID: | 490 | case HIDP_HSHK_ERR_INVALID_REPORT_ID: |
368 | case HIDP_HSHK_ERR_UNSUPPORTED_REQUEST: | 491 | case HIDP_HSHK_ERR_UNSUPPORTED_REQUEST: |
369 | case HIDP_HSHK_ERR_INVALID_PARAMETER: | 492 | case HIDP_HSHK_ERR_INVALID_PARAMETER: |
493 | if (test_bit(HIDP_WAITING_FOR_RETURN, &session->flags)) { | ||
494 | clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags); | ||
495 | wake_up_interruptible(&session->report_queue); | ||
496 | } | ||
370 | /* FIXME: Call into SET_ GET_ handlers here */ | 497 | /* FIXME: Call into SET_ GET_ handlers here */ |
371 | break; | 498 | break; |
372 | 499 | ||
@@ -385,6 +512,12 @@ static void hidp_process_handshake(struct hidp_session *session, | |||
385 | HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0); | 512 | HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0); |
386 | break; | 513 | break; |
387 | } | 514 | } |
515 | |||
516 | /* Wake up the waiting thread. */ | ||
517 | if (test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags)) { | ||
518 | clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags); | ||
519 | wake_up_interruptible(&session->report_queue); | ||
520 | } | ||
388 | } | 521 | } |
389 | 522 | ||
390 | static void hidp_process_hid_control(struct hidp_session *session, | 523 | static void hidp_process_hid_control(struct hidp_session *session, |
@@ -403,9 +536,11 @@ static void hidp_process_hid_control(struct hidp_session *session, | |||
403 | } | 536 | } |
404 | } | 537 | } |
405 | 538 | ||
406 | static void hidp_process_data(struct hidp_session *session, struct sk_buff *skb, | 539 | /* Returns true if the passed-in skb should be freed by the caller. */ |
540 | static int hidp_process_data(struct hidp_session *session, struct sk_buff *skb, | ||
407 | unsigned char param) | 541 | unsigned char param) |
408 | { | 542 | { |
543 | int done_with_skb = 1; | ||
409 | BT_DBG("session %p skb %p len %d param 0x%02x", session, skb, skb->len, param); | 544 | BT_DBG("session %p skb %p len %d param 0x%02x", session, skb, skb->len, param); |
410 | 545 | ||
411 | switch (param) { | 546 | switch (param) { |
@@ -417,7 +552,6 @@ static void hidp_process_data(struct hidp_session *session, struct sk_buff *skb, | |||
417 | 552 | ||
418 | if (session->hid) | 553 | if (session->hid) |
419 | hid_input_report(session->hid, HID_INPUT_REPORT, skb->data, skb->len, 0); | 554 | hid_input_report(session->hid, HID_INPUT_REPORT, skb->data, skb->len, 0); |
420 | |||
421 | break; | 555 | break; |
422 | 556 | ||
423 | case HIDP_DATA_RTYPE_OTHER: | 557 | case HIDP_DATA_RTYPE_OTHER: |
@@ -429,12 +563,27 @@ static void hidp_process_data(struct hidp_session *session, struct sk_buff *skb, | |||
429 | __hidp_send_ctrl_message(session, | 563 | __hidp_send_ctrl_message(session, |
430 | HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0); | 564 | HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0); |
431 | } | 565 | } |
566 | |||
567 | if (test_bit(HIDP_WAITING_FOR_RETURN, &session->flags) && | ||
568 | param == session->waiting_report_type) { | ||
569 | if (session->waiting_report_number < 0 || | ||
570 | session->waiting_report_number == skb->data[0]) { | ||
571 | /* hidp_get_raw_report() is waiting on this report. */ | ||
572 | session->report_return = skb; | ||
573 | done_with_skb = 0; | ||
574 | clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags); | ||
575 | wake_up_interruptible(&session->report_queue); | ||
576 | } | ||
577 | } | ||
578 | |||
579 | return done_with_skb; | ||
432 | } | 580 | } |
433 | 581 | ||
434 | static void hidp_recv_ctrl_frame(struct hidp_session *session, | 582 | static void hidp_recv_ctrl_frame(struct hidp_session *session, |
435 | struct sk_buff *skb) | 583 | struct sk_buff *skb) |
436 | { | 584 | { |
437 | unsigned char hdr, type, param; | 585 | unsigned char hdr, type, param; |
586 | int free_skb = 1; | ||
438 | 587 | ||
439 | BT_DBG("session %p skb %p len %d", session, skb, skb->len); | 588 | BT_DBG("session %p skb %p len %d", session, skb, skb->len); |
440 | 589 | ||
@@ -454,7 +603,7 @@ static void hidp_recv_ctrl_frame(struct hidp_session *session, | |||
454 | break; | 603 | break; |
455 | 604 | ||
456 | case HIDP_TRANS_DATA: | 605 | case HIDP_TRANS_DATA: |
457 | hidp_process_data(session, skb, param); | 606 | free_skb = hidp_process_data(session, skb, param); |
458 | break; | 607 | break; |
459 | 608 | ||
460 | default: | 609 | default: |
@@ -463,7 +612,8 @@ static void hidp_recv_ctrl_frame(struct hidp_session *session, | |||
463 | break; | 612 | break; |
464 | } | 613 | } |
465 | 614 | ||
466 | kfree_skb(skb); | 615 | if (free_skb) |
616 | kfree_skb(skb); | ||
467 | } | 617 | } |
468 | 618 | ||
469 | static void hidp_recv_intr_frame(struct hidp_session *session, | 619 | static void hidp_recv_intr_frame(struct hidp_session *session, |
@@ -563,6 +713,8 @@ static int hidp_session(void *arg) | |||
563 | init_waitqueue_entry(&intr_wait, current); | 713 | init_waitqueue_entry(&intr_wait, current); |
564 | add_wait_queue(sk_sleep(ctrl_sk), &ctrl_wait); | 714 | add_wait_queue(sk_sleep(ctrl_sk), &ctrl_wait); |
565 | add_wait_queue(sk_sleep(intr_sk), &intr_wait); | 715 | add_wait_queue(sk_sleep(intr_sk), &intr_wait); |
716 | session->waiting_for_startup = 0; | ||
717 | wake_up_interruptible(&session->startup_queue); | ||
566 | while (!atomic_read(&session->terminate)) { | 718 | while (!atomic_read(&session->terminate)) { |
567 | set_current_state(TASK_INTERRUPTIBLE); | 719 | set_current_state(TASK_INTERRUPTIBLE); |
568 | 720 | ||
@@ -754,6 +906,8 @@ static struct hid_ll_driver hidp_hid_driver = { | |||
754 | .hidinput_input_event = hidp_hidinput_event, | 906 | .hidinput_input_event = hidp_hidinput_event, |
755 | }; | 907 | }; |
756 | 908 | ||
909 | /* This function sets up the hid device. It does not add it | ||
910 | to the HID system. That is done in hidp_add_connection(). */ | ||
757 | static int hidp_setup_hid(struct hidp_session *session, | 911 | static int hidp_setup_hid(struct hidp_session *session, |
758 | struct hidp_connadd_req *req) | 912 | struct hidp_connadd_req *req) |
759 | { | 913 | { |
@@ -793,18 +947,11 @@ static int hidp_setup_hid(struct hidp_session *session, | |||
793 | hid->dev.parent = hidp_get_device(session); | 947 | hid->dev.parent = hidp_get_device(session); |
794 | hid->ll_driver = &hidp_hid_driver; | 948 | hid->ll_driver = &hidp_hid_driver; |
795 | 949 | ||
950 | hid->hid_get_raw_report = hidp_get_raw_report; | ||
796 | hid->hid_output_raw_report = hidp_output_raw_report; | 951 | hid->hid_output_raw_report = hidp_output_raw_report; |
797 | 952 | ||
798 | err = hid_add_device(hid); | ||
799 | if (err < 0) | ||
800 | goto failed; | ||
801 | |||
802 | return 0; | 953 | return 0; |
803 | 954 | ||
804 | failed: | ||
805 | hid_destroy_device(hid); | ||
806 | session->hid = NULL; | ||
807 | |||
808 | fault: | 955 | fault: |
809 | kfree(session->rd_data); | 956 | kfree(session->rd_data); |
810 | session->rd_data = NULL; | 957 | session->rd_data = NULL; |
@@ -853,6 +1000,10 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, | |||
853 | skb_queue_head_init(&session->ctrl_transmit); | 1000 | skb_queue_head_init(&session->ctrl_transmit); |
854 | skb_queue_head_init(&session->intr_transmit); | 1001 | skb_queue_head_init(&session->intr_transmit); |
855 | 1002 | ||
1003 | mutex_init(&session->report_mutex); | ||
1004 | init_waitqueue_head(&session->report_queue); | ||
1005 | init_waitqueue_head(&session->startup_queue); | ||
1006 | session->waiting_for_startup = 1; | ||
856 | session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID); | 1007 | session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID); |
857 | session->idle_to = req->idle_to; | 1008 | session->idle_to = req->idle_to; |
858 | 1009 | ||
@@ -875,6 +1026,14 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, | |||
875 | err = kernel_thread(hidp_session, session, CLONE_KERNEL); | 1026 | err = kernel_thread(hidp_session, session, CLONE_KERNEL); |
876 | if (err < 0) | 1027 | if (err < 0) |
877 | goto unlink; | 1028 | goto unlink; |
1029 | while (session->waiting_for_startup) { | ||
1030 | wait_event_interruptible(session->startup_queue, | ||
1031 | !session->waiting_for_startup); | ||
1032 | } | ||
1033 | |||
1034 | err = hid_add_device(session->hid); | ||
1035 | if (err < 0) | ||
1036 | goto err_add_device; | ||
878 | 1037 | ||
879 | if (session->input) { | 1038 | if (session->input) { |
880 | hidp_send_ctrl_message(session, | 1039 | hidp_send_ctrl_message(session, |
@@ -888,6 +1047,12 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, | |||
888 | up_write(&hidp_session_sem); | 1047 | up_write(&hidp_session_sem); |
889 | return 0; | 1048 | return 0; |
890 | 1049 | ||
1050 | err_add_device: | ||
1051 | hid_destroy_device(session->hid); | ||
1052 | session->hid = NULL; | ||
1053 | atomic_inc(&session->terminate); | ||
1054 | hidp_schedule(session); | ||
1055 | |||
891 | unlink: | 1056 | unlink: |
892 | hidp_del_timer(session); | 1057 | hidp_del_timer(session); |
893 | 1058 | ||
diff --git a/net/bluetooth/hidp/hidp.h b/net/bluetooth/hidp/hidp.h index 8d934a19da0a..13de5fa03480 100644 --- a/net/bluetooth/hidp/hidp.h +++ b/net/bluetooth/hidp/hidp.h | |||
@@ -80,6 +80,8 @@ | |||
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_RETURN 10 | ||
84 | #define HIDP_WAITING_FOR_SEND_ACK 11 | ||
83 | 85 | ||
84 | struct hidp_connadd_req { | 86 | struct hidp_connadd_req { |
85 | int ctrl_sock; // Connected control socket | 87 | int ctrl_sock; // Connected control socket |
@@ -154,9 +156,22 @@ struct hidp_session { | |||
154 | struct sk_buff_head ctrl_transmit; | 156 | struct sk_buff_head ctrl_transmit; |
155 | struct sk_buff_head intr_transmit; | 157 | struct sk_buff_head intr_transmit; |
156 | 158 | ||
159 | /* Used in hidp_get_raw_report() */ | ||
160 | int waiting_report_type; /* HIDP_DATA_RTYPE_* */ | ||
161 | int waiting_report_number; /* -1 for not numbered */ | ||
162 | struct mutex report_mutex; | ||
163 | struct sk_buff *report_return; | ||
164 | wait_queue_head_t report_queue; | ||
165 | |||
166 | /* Used in hidp_output_raw_report() */ | ||
167 | int output_report_success; /* boolean */ | ||
168 | |||
157 | /* Report descriptor */ | 169 | /* Report descriptor */ |
158 | __u8 *rd_data; | 170 | __u8 *rd_data; |
159 | uint rd_size; | 171 | uint rd_size; |
172 | |||
173 | wait_queue_head_t startup_queue; | ||
174 | int waiting_for_startup; | ||
160 | }; | 175 | }; |
161 | 176 | ||
162 | static inline void hidp_schedule(struct hidp_session *session) | 177 | static inline void hidp_schedule(struct hidp_session *session) |