diff options
Diffstat (limited to 'net/bluetooth/hidp/core.c')
-rw-r--r-- | net/bluetooth/hidp/core.c | 208 |
1 files changed, 187 insertions, 21 deletions
diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 29544c21f4b..5ec12971af6 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> |
@@ -157,7 +158,8 @@ static int hidp_queue_event(struct hidp_session *session, struct input_dev *dev, | |||
157 | 158 | ||
158 | session->leds = newleds; | 159 | session->leds = newleds; |
159 | 160 | ||
160 | if (!(skb = alloc_skb(3, GFP_ATOMIC))) { | 161 | skb = alloc_skb(3, GFP_ATOMIC); |
162 | if (!skb) { | ||
161 | BT_ERR("Can't allocate memory for new frame"); | 163 | BT_ERR("Can't allocate memory for new frame"); |
162 | return -ENOMEM; | 164 | return -ENOMEM; |
163 | } | 165 | } |
@@ -250,7 +252,8 @@ static int __hidp_send_ctrl_message(struct hidp_session *session, | |||
250 | 252 | ||
251 | BT_DBG("session %p data %p size %d", session, data, size); | 253 | BT_DBG("session %p data %p size %d", session, data, size); |
252 | 254 | ||
253 | if (!(skb = alloc_skb(size + 1, GFP_ATOMIC))) { | 255 | skb = alloc_skb(size + 1, GFP_ATOMIC); |
256 | if (!skb) { | ||
254 | BT_ERR("Can't allocate memory for new frame"); | 257 | BT_ERR("Can't allocate memory for new frame"); |
255 | return -ENOMEM; | 258 | return -ENOMEM; |
256 | } | 259 | } |
@@ -283,7 +286,8 @@ static int hidp_queue_report(struct hidp_session *session, | |||
283 | 286 | ||
284 | BT_DBG("session %p hid %p data %p size %d", session, session->hid, data, size); | 287 | BT_DBG("session %p hid %p data %p size %d", session, session->hid, data, size); |
285 | 288 | ||
286 | if (!(skb = alloc_skb(size + 1, GFP_ATOMIC))) { | 289 | skb = alloc_skb(size + 1, GFP_ATOMIC); |
290 | if (!skb) { | ||
287 | BT_ERR("Can't allocate memory for new frame"); | 291 | BT_ERR("Can't allocate memory for new frame"); |
288 | return -ENOMEM; | 292 | return -ENOMEM; |
289 | } | 293 | } |
@@ -313,24 +317,144 @@ static int hidp_send_report(struct hidp_session *session, struct hid_report *rep | |||
313 | return hidp_queue_report(session, buf, rsize); | 317 | return hidp_queue_report(session, buf, rsize); |
314 | } | 318 | } |
315 | 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 | |||
316 | 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, |
317 | unsigned char report_type) | 401 | unsigned char report_type) |
318 | { | 402 | { |
403 | struct hidp_session *session = hid->driver_data; | ||
404 | int ret; | ||
405 | |||
319 | switch (report_type) { | 406 | switch (report_type) { |
320 | case HID_FEATURE_REPORT: | 407 | case HID_FEATURE_REPORT: |
321 | report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE; | 408 | report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE; |
322 | break; | 409 | break; |
323 | case HID_OUTPUT_REPORT: | 410 | case HID_OUTPUT_REPORT: |
324 | report_type = HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT; | 411 | report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_OUPUT; |
325 | break; | 412 | break; |
326 | default: | 413 | default: |
327 | return -EINVAL; | 414 | return -EINVAL; |
328 | } | 415 | } |
329 | 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); | ||
330 | if (hidp_send_ctrl_message(hid->driver_data, report_type, | 422 | if (hidp_send_ctrl_message(hid->driver_data, report_type, |
331 | data, count)) | 423 | data, count)) { |
332 | return -ENOMEM; | 424 | ret = -ENOMEM; |
333 | 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; | ||
334 | } | 458 | } |
335 | 459 | ||
336 | static void hidp_idle_timeout(unsigned long arg) | 460 | static void hidp_idle_timeout(unsigned long arg) |
@@ -357,16 +481,22 @@ static void hidp_process_handshake(struct hidp_session *session, | |||
357 | unsigned char param) | 481 | unsigned char param) |
358 | { | 482 | { |
359 | 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 */ | ||
360 | 485 | ||
361 | switch (param) { | 486 | switch (param) { |
362 | case HIDP_HSHK_SUCCESSFUL: | 487 | case HIDP_HSHK_SUCCESSFUL: |
363 | /* FIXME: Call into SET_ GET_ handlers here */ | 488 | /* FIXME: Call into SET_ GET_ handlers here */ |
489 | session->output_report_success = 1; | ||
364 | break; | 490 | break; |
365 | 491 | ||
366 | case HIDP_HSHK_NOT_READY: | 492 | case HIDP_HSHK_NOT_READY: |
367 | case HIDP_HSHK_ERR_INVALID_REPORT_ID: | 493 | case HIDP_HSHK_ERR_INVALID_REPORT_ID: |
368 | case HIDP_HSHK_ERR_UNSUPPORTED_REQUEST: | 494 | case HIDP_HSHK_ERR_UNSUPPORTED_REQUEST: |
369 | 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 | } | ||
370 | /* FIXME: Call into SET_ GET_ handlers here */ | 500 | /* FIXME: Call into SET_ GET_ handlers here */ |
371 | break; | 501 | break; |
372 | 502 | ||
@@ -385,6 +515,12 @@ static void hidp_process_handshake(struct hidp_session *session, | |||
385 | HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0); | 515 | HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0); |
386 | break; | 516 | break; |
387 | } | 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 | } | ||
388 | } | 524 | } |
389 | 525 | ||
390 | static void hidp_process_hid_control(struct hidp_session *session, | 526 | static void hidp_process_hid_control(struct hidp_session *session, |
@@ -403,9 +539,11 @@ static void hidp_process_hid_control(struct hidp_session *session, | |||
403 | } | 539 | } |
404 | } | 540 | } |
405 | 541 | ||
406 | 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, | ||
407 | unsigned char param) | 544 | unsigned char param) |
408 | { | 545 | { |
546 | int done_with_skb = 1; | ||
409 | 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); |
410 | 548 | ||
411 | switch (param) { | 549 | switch (param) { |
@@ -417,7 +555,6 @@ static void hidp_process_data(struct hidp_session *session, struct sk_buff *skb, | |||
417 | 555 | ||
418 | if (session->hid) | 556 | if (session->hid) |
419 | 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); |
420 | |||
421 | break; | 558 | break; |
422 | 559 | ||
423 | case HIDP_DATA_RTYPE_OTHER: | 560 | case HIDP_DATA_RTYPE_OTHER: |
@@ -429,12 +566,27 @@ static void hidp_process_data(struct hidp_session *session, struct sk_buff *skb, | |||
429 | __hidp_send_ctrl_message(session, | 566 | __hidp_send_ctrl_message(session, |
430 | HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0); | 567 | HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0); |
431 | } | 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; | ||
432 | } | 583 | } |
433 | 584 | ||
434 | static void hidp_recv_ctrl_frame(struct hidp_session *session, | 585 | static void hidp_recv_ctrl_frame(struct hidp_session *session, |
435 | struct sk_buff *skb) | 586 | struct sk_buff *skb) |
436 | { | 587 | { |
437 | unsigned char hdr, type, param; | 588 | unsigned char hdr, type, param; |
589 | int free_skb = 1; | ||
438 | 590 | ||
439 | 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); |
440 | 592 | ||
@@ -454,7 +606,7 @@ static void hidp_recv_ctrl_frame(struct hidp_session *session, | |||
454 | break; | 606 | break; |
455 | 607 | ||
456 | case HIDP_TRANS_DATA: | 608 | case HIDP_TRANS_DATA: |
457 | hidp_process_data(session, skb, param); | 609 | free_skb = hidp_process_data(session, skb, param); |
458 | break; | 610 | break; |
459 | 611 | ||
460 | default: | 612 | default: |
@@ -463,7 +615,8 @@ static void hidp_recv_ctrl_frame(struct hidp_session *session, | |||
463 | break; | 615 | break; |
464 | } | 616 | } |
465 | 617 | ||
466 | kfree_skb(skb); | 618 | if (free_skb) |
619 | kfree_skb(skb); | ||
467 | } | 620 | } |
468 | 621 | ||
469 | static void hidp_recv_intr_frame(struct hidp_session *session, | 622 | static void hidp_recv_intr_frame(struct hidp_session *session, |
@@ -563,6 +716,8 @@ static int hidp_session(void *arg) | |||
563 | init_waitqueue_entry(&intr_wait, current); | 716 | init_waitqueue_entry(&intr_wait, current); |
564 | add_wait_queue(sk_sleep(ctrl_sk), &ctrl_wait); | 717 | add_wait_queue(sk_sleep(ctrl_sk), &ctrl_wait); |
565 | 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); | ||
566 | while (!atomic_read(&session->terminate)) { | 721 | while (!atomic_read(&session->terminate)) { |
567 | set_current_state(TASK_INTERRUPTIBLE); | 722 | set_current_state(TASK_INTERRUPTIBLE); |
568 | 723 | ||
@@ -754,6 +909,8 @@ static struct hid_ll_driver hidp_hid_driver = { | |||
754 | .hidinput_input_event = hidp_hidinput_event, | 909 | .hidinput_input_event = hidp_hidinput_event, |
755 | }; | 910 | }; |
756 | 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(). */ | ||
757 | static int hidp_setup_hid(struct hidp_session *session, | 914 | static int hidp_setup_hid(struct hidp_session *session, |
758 | struct hidp_connadd_req *req) | 915 | struct hidp_connadd_req *req) |
759 | { | 916 | { |
@@ -793,18 +950,11 @@ static int hidp_setup_hid(struct hidp_session *session, | |||
793 | hid->dev.parent = hidp_get_device(session); | 950 | hid->dev.parent = hidp_get_device(session); |
794 | hid->ll_driver = &hidp_hid_driver; | 951 | hid->ll_driver = &hidp_hid_driver; |
795 | 952 | ||
953 | hid->hid_get_raw_report = hidp_get_raw_report; | ||
796 | hid->hid_output_raw_report = hidp_output_raw_report; | 954 | hid->hid_output_raw_report = hidp_output_raw_report; |
797 | 955 | ||
798 | err = hid_add_device(hid); | ||
799 | if (err < 0) | ||
800 | goto failed; | ||
801 | |||
802 | return 0; | 956 | return 0; |
803 | 957 | ||
804 | failed: | ||
805 | hid_destroy_device(hid); | ||
806 | session->hid = NULL; | ||
807 | |||
808 | fault: | 958 | fault: |
809 | kfree(session->rd_data); | 959 | kfree(session->rd_data); |
810 | session->rd_data = NULL; | 960 | session->rd_data = NULL; |
@@ -853,6 +1003,10 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, | |||
853 | skb_queue_head_init(&session->ctrl_transmit); | 1003 | skb_queue_head_init(&session->ctrl_transmit); |
854 | skb_queue_head_init(&session->intr_transmit); | 1004 | skb_queue_head_init(&session->intr_transmit); |
855 | 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; | ||
856 | session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID); | 1010 | session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID); |
857 | session->idle_to = req->idle_to; | 1011 | session->idle_to = req->idle_to; |
858 | 1012 | ||
@@ -875,6 +1029,14 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, | |||
875 | err = kernel_thread(hidp_session, session, CLONE_KERNEL); | 1029 | err = kernel_thread(hidp_session, session, CLONE_KERNEL); |
876 | if (err < 0) | 1030 | if (err < 0) |
877 | 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; | ||
878 | 1040 | ||
879 | if (session->input) { | 1041 | if (session->input) { |
880 | hidp_send_ctrl_message(session, | 1042 | hidp_send_ctrl_message(session, |
@@ -888,6 +1050,12 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, | |||
888 | up_write(&hidp_session_sem); | 1050 | up_write(&hidp_session_sem); |
889 | return 0; | 1051 | return 0; |
890 | 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 | |||
891 | unlink: | 1059 | unlink: |
892 | hidp_del_timer(session); | 1060 | hidp_del_timer(session); |
893 | 1061 | ||
@@ -1016,8 +1184,6 @@ static int __init hidp_init(void) | |||
1016 | { | 1184 | { |
1017 | int ret; | 1185 | int ret; |
1018 | 1186 | ||
1019 | l2cap_load(); | ||
1020 | |||
1021 | BT_INFO("HIDP (Human Interface Emulation) ver %s", VERSION); | 1187 | BT_INFO("HIDP (Human Interface Emulation) ver %s", VERSION); |
1022 | 1188 | ||
1023 | ret = hid_register_driver(&hidp_driver); | 1189 | ret = hid_register_driver(&hidp_driver); |