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 2429ca2d7b06..5ec12971af6b 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> |
@@ -316,24 +317,144 @@ static int hidp_send_report(struct hidp_session *session, struct hid_report *rep | |||
316 | return hidp_queue_report(session, buf, rsize); | 317 | return hidp_queue_report(session, buf, rsize); |
317 | } | 318 | } |
318 | 319 | ||
320 | static int hidp_get_raw_report(struct hid_device *hid, | ||
321 | unsigned char report_number, | ||
322 | unsigned char *data, size_t count, | ||
323 | unsigned char report_type) | ||
324 | { | ||
325 | struct hidp_session *session = hid->driver_data; | ||
326 | struct sk_buff *skb; | ||
327 | size_t len; | ||
328 | int numbered_reports = hid->report_enum[report_type].numbered; | ||
329 | |||
330 | switch (report_type) { | ||
331 | case HID_FEATURE_REPORT: | ||
332 | report_type = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_FEATURE; | ||
333 | break; | ||
334 | case HID_INPUT_REPORT: | ||
335 | report_type = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_INPUT; | ||
336 | break; | ||
337 | case HID_OUTPUT_REPORT: | ||
338 | report_type = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_OUPUT; | ||
339 | break; | ||
340 | default: | ||
341 | return -EINVAL; | ||
342 | } | ||
343 | |||
344 | if (mutex_lock_interruptible(&session->report_mutex)) | ||
345 | return -ERESTARTSYS; | ||
346 | |||
347 | /* Set up our wait, and send the report request to the device. */ | ||
348 | session->waiting_report_type = report_type & HIDP_DATA_RTYPE_MASK; | ||
349 | session->waiting_report_number = numbered_reports ? report_number : -1; | ||
350 | set_bit(HIDP_WAITING_FOR_RETURN, &session->flags); | ||
351 | data[0] = report_number; | ||
352 | if (hidp_send_ctrl_message(hid->driver_data, report_type, data, 1)) | ||
353 | goto err_eio; | ||
354 | |||
355 | /* Wait for the return of the report. The returned report | ||
356 | gets put in session->report_return. */ | ||
357 | while (test_bit(HIDP_WAITING_FOR_RETURN, &session->flags)) { | ||
358 | int res; | ||
359 | |||
360 | res = wait_event_interruptible_timeout(session->report_queue, | ||
361 | !test_bit(HIDP_WAITING_FOR_RETURN, &session->flags), | ||
362 | 5*HZ); | ||
363 | if (res == 0) { | ||
364 | /* timeout */ | ||
365 | goto err_eio; | ||
366 | } | ||
367 | if (res < 0) { | ||
368 | /* signal */ | ||
369 | goto err_restartsys; | ||
370 | } | ||
371 | } | ||
372 | |||
373 | skb = session->report_return; | ||
374 | if (skb) { | ||
375 | len = skb->len < count ? skb->len : count; | ||
376 | memcpy(data, skb->data, len); | ||
377 | |||
378 | kfree_skb(skb); | ||
379 | session->report_return = NULL; | ||
380 | } else { | ||
381 | /* Device returned a HANDSHAKE, indicating protocol error. */ | ||
382 | len = -EIO; | ||
383 | } | ||
384 | |||
385 | clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags); | ||
386 | mutex_unlock(&session->report_mutex); | ||
387 | |||
388 | return len; | ||
389 | |||
390 | err_restartsys: | ||
391 | clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags); | ||
392 | mutex_unlock(&session->report_mutex); | ||
393 | return -ERESTARTSYS; | ||
394 | err_eio: | ||
395 | clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags); | ||
396 | mutex_unlock(&session->report_mutex); | ||
397 | return -EIO; | ||
398 | } | ||
399 | |||
319 | static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, size_t count, | 400 | static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, size_t count, |
320 | unsigned char report_type) | 401 | unsigned char report_type) |
321 | { | 402 | { |
403 | struct hidp_session *session = hid->driver_data; | ||
404 | int ret; | ||
405 | |||
322 | switch (report_type) { | 406 | switch (report_type) { |
323 | case HID_FEATURE_REPORT: | 407 | case HID_FEATURE_REPORT: |
324 | report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE; | 408 | report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE; |
325 | break; | 409 | break; |
326 | case HID_OUTPUT_REPORT: | 410 | case HID_OUTPUT_REPORT: |
327 | report_type = HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT; | 411 | report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_OUPUT; |
328 | break; | 412 | break; |
329 | default: | 413 | default: |
330 | return -EINVAL; | 414 | return -EINVAL; |
331 | } | 415 | } |
332 | 416 | ||
417 | if (mutex_lock_interruptible(&session->report_mutex)) | ||
418 | return -ERESTARTSYS; | ||
419 | |||
420 | /* Set up our wait, and send the report request to the device. */ | ||
421 | set_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags); | ||
333 | if (hidp_send_ctrl_message(hid->driver_data, report_type, | 422 | if (hidp_send_ctrl_message(hid->driver_data, report_type, |
334 | data, count)) | 423 | data, count)) { |
335 | return -ENOMEM; | 424 | ret = -ENOMEM; |
336 | return count; | 425 | goto err; |
426 | } | ||
427 | |||
428 | /* Wait for the ACK from the device. */ | ||
429 | while (test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags)) { | ||
430 | int res; | ||
431 | |||
432 | res = wait_event_interruptible_timeout(session->report_queue, | ||
433 | !test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags), | ||
434 | 10*HZ); | ||
435 | if (res == 0) { | ||
436 | /* timeout */ | ||
437 | ret = -EIO; | ||
438 | goto err; | ||
439 | } | ||
440 | if (res < 0) { | ||
441 | /* signal */ | ||
442 | ret = -ERESTARTSYS; | ||
443 | goto err; | ||
444 | } | ||
445 | } | ||
446 | |||
447 | if (!session->output_report_success) { | ||
448 | ret = -EIO; | ||
449 | goto err; | ||
450 | } | ||
451 | |||
452 | ret = count; | ||
453 | |||
454 | err: | ||
455 | clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags); | ||
456 | mutex_unlock(&session->report_mutex); | ||
457 | return ret; | ||
337 | } | 458 | } |
338 | 459 | ||
339 | static void hidp_idle_timeout(unsigned long arg) | 460 | static void hidp_idle_timeout(unsigned long arg) |
@@ -360,16 +481,22 @@ static void hidp_process_handshake(struct hidp_session *session, | |||
360 | unsigned char param) | 481 | unsigned char param) |
361 | { | 482 | { |
362 | BT_DBG("session %p param 0x%02x", session, param); | 483 | BT_DBG("session %p param 0x%02x", session, param); |
484 | session->output_report_success = 0; /* default condition */ | ||
363 | 485 | ||
364 | switch (param) { | 486 | switch (param) { |
365 | case HIDP_HSHK_SUCCESSFUL: | 487 | case HIDP_HSHK_SUCCESSFUL: |
366 | /* FIXME: Call into SET_ GET_ handlers here */ | 488 | /* FIXME: Call into SET_ GET_ handlers here */ |
489 | session->output_report_success = 1; | ||
367 | break; | 490 | break; |
368 | 491 | ||
369 | case HIDP_HSHK_NOT_READY: | 492 | case HIDP_HSHK_NOT_READY: |
370 | case HIDP_HSHK_ERR_INVALID_REPORT_ID: | 493 | case HIDP_HSHK_ERR_INVALID_REPORT_ID: |
371 | case HIDP_HSHK_ERR_UNSUPPORTED_REQUEST: | 494 | case HIDP_HSHK_ERR_UNSUPPORTED_REQUEST: |
372 | case HIDP_HSHK_ERR_INVALID_PARAMETER: | 495 | case HIDP_HSHK_ERR_INVALID_PARAMETER: |
496 | if (test_bit(HIDP_WAITING_FOR_RETURN, &session->flags)) { | ||
497 | clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags); | ||
498 | wake_up_interruptible(&session->report_queue); | ||
499 | } | ||
373 | /* FIXME: Call into SET_ GET_ handlers here */ | 500 | /* FIXME: Call into SET_ GET_ handlers here */ |
374 | break; | 501 | break; |
375 | 502 | ||
@@ -388,6 +515,12 @@ static void hidp_process_handshake(struct hidp_session *session, | |||
388 | HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0); | 515 | HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0); |
389 | break; | 516 | break; |
390 | } | 517 | } |
518 | |||
519 | /* Wake up the waiting thread. */ | ||
520 | if (test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags)) { | ||
521 | clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags); | ||
522 | wake_up_interruptible(&session->report_queue); | ||
523 | } | ||
391 | } | 524 | } |
392 | 525 | ||
393 | static void hidp_process_hid_control(struct hidp_session *session, | 526 | static void hidp_process_hid_control(struct hidp_session *session, |
@@ -406,9 +539,11 @@ static void hidp_process_hid_control(struct hidp_session *session, | |||
406 | } | 539 | } |
407 | } | 540 | } |
408 | 541 | ||
409 | static void hidp_process_data(struct hidp_session *session, struct sk_buff *skb, | 542 | /* Returns true if the passed-in skb should be freed by the caller. */ |
543 | static int hidp_process_data(struct hidp_session *session, struct sk_buff *skb, | ||
410 | unsigned char param) | 544 | unsigned char param) |
411 | { | 545 | { |
546 | int done_with_skb = 1; | ||
412 | BT_DBG("session %p skb %p len %d param 0x%02x", session, skb, skb->len, param); | 547 | BT_DBG("session %p skb %p len %d param 0x%02x", session, skb, skb->len, param); |
413 | 548 | ||
414 | switch (param) { | 549 | switch (param) { |
@@ -420,7 +555,6 @@ static void hidp_process_data(struct hidp_session *session, struct sk_buff *skb, | |||
420 | 555 | ||
421 | if (session->hid) | 556 | if (session->hid) |
422 | hid_input_report(session->hid, HID_INPUT_REPORT, skb->data, skb->len, 0); | 557 | hid_input_report(session->hid, HID_INPUT_REPORT, skb->data, skb->len, 0); |
423 | |||
424 | break; | 558 | break; |
425 | 559 | ||
426 | case HIDP_DATA_RTYPE_OTHER: | 560 | case HIDP_DATA_RTYPE_OTHER: |
@@ -432,12 +566,27 @@ static void hidp_process_data(struct hidp_session *session, struct sk_buff *skb, | |||
432 | __hidp_send_ctrl_message(session, | 566 | __hidp_send_ctrl_message(session, |
433 | HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0); | 567 | HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0); |
434 | } | 568 | } |
569 | |||
570 | if (test_bit(HIDP_WAITING_FOR_RETURN, &session->flags) && | ||
571 | param == session->waiting_report_type) { | ||
572 | if (session->waiting_report_number < 0 || | ||
573 | session->waiting_report_number == skb->data[0]) { | ||
574 | /* hidp_get_raw_report() is waiting on this report. */ | ||
575 | session->report_return = skb; | ||
576 | done_with_skb = 0; | ||
577 | clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags); | ||
578 | wake_up_interruptible(&session->report_queue); | ||
579 | } | ||
580 | } | ||
581 | |||
582 | return done_with_skb; | ||
435 | } | 583 | } |
436 | 584 | ||
437 | static void hidp_recv_ctrl_frame(struct hidp_session *session, | 585 | static void hidp_recv_ctrl_frame(struct hidp_session *session, |
438 | struct sk_buff *skb) | 586 | struct sk_buff *skb) |
439 | { | 587 | { |
440 | unsigned char hdr, type, param; | 588 | unsigned char hdr, type, param; |
589 | int free_skb = 1; | ||
441 | 590 | ||
442 | BT_DBG("session %p skb %p len %d", session, skb, skb->len); | 591 | BT_DBG("session %p skb %p len %d", session, skb, skb->len); |
443 | 592 | ||
@@ -457,7 +606,7 @@ static void hidp_recv_ctrl_frame(struct hidp_session *session, | |||
457 | break; | 606 | break; |
458 | 607 | ||
459 | case HIDP_TRANS_DATA: | 608 | case HIDP_TRANS_DATA: |
460 | hidp_process_data(session, skb, param); | 609 | free_skb = hidp_process_data(session, skb, param); |
461 | break; | 610 | break; |
462 | 611 | ||
463 | default: | 612 | default: |
@@ -466,7 +615,8 @@ static void hidp_recv_ctrl_frame(struct hidp_session *session, | |||
466 | break; | 615 | break; |
467 | } | 616 | } |
468 | 617 | ||
469 | kfree_skb(skb); | 618 | if (free_skb) |
619 | kfree_skb(skb); | ||
470 | } | 620 | } |
471 | 621 | ||
472 | static void hidp_recv_intr_frame(struct hidp_session *session, | 622 | static void hidp_recv_intr_frame(struct hidp_session *session, |
@@ -566,6 +716,8 @@ static int hidp_session(void *arg) | |||
566 | init_waitqueue_entry(&intr_wait, current); | 716 | init_waitqueue_entry(&intr_wait, current); |
567 | add_wait_queue(sk_sleep(ctrl_sk), &ctrl_wait); | 717 | add_wait_queue(sk_sleep(ctrl_sk), &ctrl_wait); |
568 | add_wait_queue(sk_sleep(intr_sk), &intr_wait); | 718 | add_wait_queue(sk_sleep(intr_sk), &intr_wait); |
719 | session->waiting_for_startup = 0; | ||
720 | wake_up_interruptible(&session->startup_queue); | ||
569 | while (!atomic_read(&session->terminate)) { | 721 | while (!atomic_read(&session->terminate)) { |
570 | set_current_state(TASK_INTERRUPTIBLE); | 722 | set_current_state(TASK_INTERRUPTIBLE); |
571 | 723 | ||
@@ -757,6 +909,8 @@ static struct hid_ll_driver hidp_hid_driver = { | |||
757 | .hidinput_input_event = hidp_hidinput_event, | 909 | .hidinput_input_event = hidp_hidinput_event, |
758 | }; | 910 | }; |
759 | 911 | ||
912 | /* This function sets up the hid device. It does not add it | ||
913 | to the HID system. That is done in hidp_add_connection(). */ | ||
760 | static int hidp_setup_hid(struct hidp_session *session, | 914 | static int hidp_setup_hid(struct hidp_session *session, |
761 | struct hidp_connadd_req *req) | 915 | struct hidp_connadd_req *req) |
762 | { | 916 | { |
@@ -796,18 +950,11 @@ static int hidp_setup_hid(struct hidp_session *session, | |||
796 | hid->dev.parent = hidp_get_device(session); | 950 | hid->dev.parent = hidp_get_device(session); |
797 | hid->ll_driver = &hidp_hid_driver; | 951 | hid->ll_driver = &hidp_hid_driver; |
798 | 952 | ||
953 | hid->hid_get_raw_report = hidp_get_raw_report; | ||
799 | hid->hid_output_raw_report = hidp_output_raw_report; | 954 | hid->hid_output_raw_report = hidp_output_raw_report; |
800 | 955 | ||
801 | err = hid_add_device(hid); | ||
802 | if (err < 0) | ||
803 | goto failed; | ||
804 | |||
805 | return 0; | 956 | return 0; |
806 | 957 | ||
807 | failed: | ||
808 | hid_destroy_device(hid); | ||
809 | session->hid = NULL; | ||
810 | |||
811 | fault: | 958 | fault: |
812 | kfree(session->rd_data); | 959 | kfree(session->rd_data); |
813 | session->rd_data = NULL; | 960 | session->rd_data = NULL; |
@@ -856,6 +1003,10 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, | |||
856 | skb_queue_head_init(&session->ctrl_transmit); | 1003 | skb_queue_head_init(&session->ctrl_transmit); |
857 | skb_queue_head_init(&session->intr_transmit); | 1004 | skb_queue_head_init(&session->intr_transmit); |
858 | 1005 | ||
1006 | mutex_init(&session->report_mutex); | ||
1007 | init_waitqueue_head(&session->report_queue); | ||
1008 | init_waitqueue_head(&session->startup_queue); | ||
1009 | session->waiting_for_startup = 1; | ||
859 | session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID); | 1010 | session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID); |
860 | session->idle_to = req->idle_to; | 1011 | session->idle_to = req->idle_to; |
861 | 1012 | ||
@@ -878,6 +1029,14 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, | |||
878 | err = kernel_thread(hidp_session, session, CLONE_KERNEL); | 1029 | err = kernel_thread(hidp_session, session, CLONE_KERNEL); |
879 | if (err < 0) | 1030 | if (err < 0) |
880 | goto unlink; | 1031 | goto unlink; |
1032 | while (session->waiting_for_startup) { | ||
1033 | wait_event_interruptible(session->startup_queue, | ||
1034 | !session->waiting_for_startup); | ||
1035 | } | ||
1036 | |||
1037 | err = hid_add_device(session->hid); | ||
1038 | if (err < 0) | ||
1039 | goto err_add_device; | ||
881 | 1040 | ||
882 | if (session->input) { | 1041 | if (session->input) { |
883 | hidp_send_ctrl_message(session, | 1042 | hidp_send_ctrl_message(session, |
@@ -891,6 +1050,12 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, | |||
891 | up_write(&hidp_session_sem); | 1050 | up_write(&hidp_session_sem); |
892 | return 0; | 1051 | return 0; |
893 | 1052 | ||
1053 | err_add_device: | ||
1054 | hid_destroy_device(session->hid); | ||
1055 | session->hid = NULL; | ||
1056 | atomic_inc(&session->terminate); | ||
1057 | hidp_schedule(session); | ||
1058 | |||
894 | unlink: | 1059 | unlink: |
895 | hidp_del_timer(session); | 1060 | hidp_del_timer(session); |
896 | 1061 | ||
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) |