diff options
author | Johan Hedberg <johan.hedberg@nokia.com> | 2011-03-28 07:07:23 -0400 |
---|---|---|
committer | Gustavo F. Padovan <padovan@profusion.mobi> | 2011-03-31 13:22:58 -0400 |
commit | 80a1e1dbf62a08984d4c1bfb5a4bca38c3e1664f (patch) | |
tree | b57c691a346e05ea76c7df70f23dabd4e05221f6 /net/bluetooth/mgmt.c | |
parent | e90165be9a4d6a1e8fa632fcae00a5294abd3981 (diff) |
Bluetooth: Add local Extended Inquiry Response (EIR) support
This patch adds automated creation of the local EIR data based on what
16-bit UUIDs are registered and what the device name is. This should
cover the majority use cases, however things like 32/128-bit UUIDs, TX
power and Device ID will need to be added later to be on par with what
bluetoothd is capable of doing (without the Management interface).
Signed-off-by: Johan Hedberg <johan.hedberg@nokia.com>
Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
Diffstat (limited to 'net/bluetooth/mgmt.c')
-rw-r--r-- | net/bluetooth/mgmt.c | 163 |
1 files changed, 163 insertions, 0 deletions
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index a42dc8ca0a6f..62055c9a8084 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c | |||
@@ -544,6 +544,150 @@ failed: | |||
544 | return err; | 544 | return err; |
545 | } | 545 | } |
546 | 546 | ||
547 | #define EIR_FLAGS 0x01 /* flags */ | ||
548 | #define EIR_UUID16_SOME 0x02 /* 16-bit UUID, more available */ | ||
549 | #define EIR_UUID16_ALL 0x03 /* 16-bit UUID, all listed */ | ||
550 | #define EIR_UUID32_SOME 0x04 /* 32-bit UUID, more available */ | ||
551 | #define EIR_UUID32_ALL 0x05 /* 32-bit UUID, all listed */ | ||
552 | #define EIR_UUID128_SOME 0x06 /* 128-bit UUID, more available */ | ||
553 | #define EIR_UUID128_ALL 0x07 /* 128-bit UUID, all listed */ | ||
554 | #define EIR_NAME_SHORT 0x08 /* shortened local name */ | ||
555 | #define EIR_NAME_COMPLETE 0x09 /* complete local name */ | ||
556 | #define EIR_TX_POWER 0x0A /* transmit power level */ | ||
557 | #define EIR_DEVICE_ID 0x10 /* device ID */ | ||
558 | |||
559 | #define PNP_INFO_SVCLASS_ID 0x1200 | ||
560 | |||
561 | static u8 bluetooth_base_uuid[] = { | ||
562 | 0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80, | ||
563 | 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
564 | }; | ||
565 | |||
566 | static u16 get_uuid16(u8 *uuid128) | ||
567 | { | ||
568 | u32 val; | ||
569 | int i; | ||
570 | |||
571 | for (i = 0; i < 12; i++) { | ||
572 | if (bluetooth_base_uuid[i] != uuid128[i]) | ||
573 | return 0; | ||
574 | } | ||
575 | |||
576 | memcpy(&val, &uuid128[12], 4); | ||
577 | |||
578 | val = le32_to_cpu(val); | ||
579 | if (val > 0xffff) | ||
580 | return 0; | ||
581 | |||
582 | return (u16) val; | ||
583 | } | ||
584 | |||
585 | static void create_eir(struct hci_dev *hdev, u8 *data) | ||
586 | { | ||
587 | u8 *ptr = data; | ||
588 | u16 eir_len = 0; | ||
589 | u16 uuid16_list[HCI_MAX_EIR_LENGTH / sizeof(u16)]; | ||
590 | int i, truncated = 0; | ||
591 | struct list_head *p; | ||
592 | size_t name_len; | ||
593 | |||
594 | name_len = strlen(hdev->dev_name); | ||
595 | |||
596 | if (name_len > 0) { | ||
597 | /* EIR Data type */ | ||
598 | if (name_len > 48) { | ||
599 | name_len = 48; | ||
600 | ptr[1] = EIR_NAME_SHORT; | ||
601 | } else | ||
602 | ptr[1] = EIR_NAME_COMPLETE; | ||
603 | |||
604 | /* EIR Data length */ | ||
605 | ptr[0] = name_len + 1; | ||
606 | |||
607 | memcpy(ptr + 2, hdev->dev_name, name_len); | ||
608 | |||
609 | eir_len += (name_len + 2); | ||
610 | ptr += (name_len + 2); | ||
611 | } | ||
612 | |||
613 | memset(uuid16_list, 0, sizeof(uuid16_list)); | ||
614 | |||
615 | /* Group all UUID16 types */ | ||
616 | list_for_each(p, &hdev->uuids) { | ||
617 | struct bt_uuid *uuid = list_entry(p, struct bt_uuid, list); | ||
618 | u16 uuid16; | ||
619 | |||
620 | uuid16 = get_uuid16(uuid->uuid); | ||
621 | if (uuid16 == 0) | ||
622 | return; | ||
623 | |||
624 | if (uuid16 < 0x1100) | ||
625 | continue; | ||
626 | |||
627 | if (uuid16 == PNP_INFO_SVCLASS_ID) | ||
628 | continue; | ||
629 | |||
630 | /* Stop if not enough space to put next UUID */ | ||
631 | if (eir_len + 2 + sizeof(u16) > HCI_MAX_EIR_LENGTH) { | ||
632 | truncated = 1; | ||
633 | break; | ||
634 | } | ||
635 | |||
636 | /* Check for duplicates */ | ||
637 | for (i = 0; uuid16_list[i] != 0; i++) | ||
638 | if (uuid16_list[i] == uuid16) | ||
639 | break; | ||
640 | |||
641 | if (uuid16_list[i] == 0) { | ||
642 | uuid16_list[i] = uuid16; | ||
643 | eir_len += sizeof(u16); | ||
644 | } | ||
645 | } | ||
646 | |||
647 | if (uuid16_list[0] != 0) { | ||
648 | u8 *length = ptr; | ||
649 | |||
650 | /* EIR Data type */ | ||
651 | ptr[1] = truncated ? EIR_UUID16_SOME : EIR_UUID16_ALL; | ||
652 | |||
653 | ptr += 2; | ||
654 | eir_len += 2; | ||
655 | |||
656 | for (i = 0; uuid16_list[i] != 0; i++) { | ||
657 | *ptr++ = (uuid16_list[i] & 0x00ff); | ||
658 | *ptr++ = (uuid16_list[i] & 0xff00) >> 8; | ||
659 | } | ||
660 | |||
661 | /* EIR Data length */ | ||
662 | *length = (i * sizeof(u16)) + 1; | ||
663 | } | ||
664 | } | ||
665 | |||
666 | static int update_eir(struct hci_dev *hdev) | ||
667 | { | ||
668 | struct hci_cp_write_eir cp; | ||
669 | |||
670 | if (!(hdev->features[6] & LMP_EXT_INQ)) | ||
671 | return 0; | ||
672 | |||
673 | if (hdev->ssp_mode == 0) | ||
674 | return 0; | ||
675 | |||
676 | if (test_bit(HCI_SERVICE_CACHE, &hdev->flags)) | ||
677 | return 0; | ||
678 | |||
679 | memset(&cp, 0, sizeof(cp)); | ||
680 | |||
681 | create_eir(hdev, cp.data); | ||
682 | |||
683 | if (memcmp(cp.data, hdev->eir, sizeof(cp.data)) == 0) | ||
684 | return 0; | ||
685 | |||
686 | memcpy(hdev->eir, cp.data, sizeof(cp.data)); | ||
687 | |||
688 | return hci_send_cmd(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp); | ||
689 | } | ||
690 | |||
547 | static u8 get_service_classes(struct hci_dev *hdev) | 691 | static u8 get_service_classes(struct hci_dev *hdev) |
548 | { | 692 | { |
549 | struct list_head *p; | 693 | struct list_head *p; |
@@ -612,6 +756,10 @@ static int add_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len) | |||
612 | if (err < 0) | 756 | if (err < 0) |
613 | goto failed; | 757 | goto failed; |
614 | 758 | ||
759 | err = update_eir(hdev); | ||
760 | if (err < 0) | ||
761 | goto failed; | ||
762 | |||
615 | err = cmd_complete(sk, index, MGMT_OP_ADD_UUID, NULL, 0); | 763 | err = cmd_complete(sk, index, MGMT_OP_ADD_UUID, NULL, 0); |
616 | 764 | ||
617 | failed: | 765 | failed: |
@@ -668,6 +816,10 @@ static int remove_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len) | |||
668 | if (err < 0) | 816 | if (err < 0) |
669 | goto unlock; | 817 | goto unlock; |
670 | 818 | ||
819 | err = update_eir(hdev); | ||
820 | if (err < 0) | ||
821 | goto unlock; | ||
822 | |||
671 | err = cmd_complete(sk, index, MGMT_OP_REMOVE_UUID, NULL, 0); | 823 | err = cmd_complete(sk, index, MGMT_OP_REMOVE_UUID, NULL, 0); |
672 | 824 | ||
673 | unlock: | 825 | unlock: |
@@ -737,6 +889,8 @@ static int set_service_cache(struct sock *sk, u16 index, unsigned char *data, | |||
737 | } else { | 889 | } else { |
738 | clear_bit(HCI_SERVICE_CACHE, &hdev->flags); | 890 | clear_bit(HCI_SERVICE_CACHE, &hdev->flags); |
739 | err = update_class(hdev); | 891 | err = update_class(hdev); |
892 | if (err == 0) | ||
893 | err = update_eir(hdev); | ||
740 | } | 894 | } |
741 | 895 | ||
742 | if (err == 0) | 896 | if (err == 0) |
@@ -1822,6 +1976,7 @@ int mgmt_auth_failed(u16 index, bdaddr_t *bdaddr, u8 status) | |||
1822 | int mgmt_set_local_name_complete(u16 index, u8 *name, u8 status) | 1976 | int mgmt_set_local_name_complete(u16 index, u8 *name, u8 status) |
1823 | { | 1977 | { |
1824 | struct pending_cmd *cmd; | 1978 | struct pending_cmd *cmd; |
1979 | struct hci_dev *hdev; | ||
1825 | struct mgmt_cp_set_local_name ev; | 1980 | struct mgmt_cp_set_local_name ev; |
1826 | int err; | 1981 | int err; |
1827 | 1982 | ||
@@ -1837,6 +1992,14 @@ int mgmt_set_local_name_complete(u16 index, u8 *name, u8 status) | |||
1837 | goto failed; | 1992 | goto failed; |
1838 | } | 1993 | } |
1839 | 1994 | ||
1995 | hdev = hci_dev_get(index); | ||
1996 | if (hdev) { | ||
1997 | hci_dev_lock_bh(hdev); | ||
1998 | update_eir(hdev); | ||
1999 | hci_dev_unlock_bh(hdev); | ||
2000 | hci_dev_put(hdev); | ||
2001 | } | ||
2002 | |||
1840 | err = cmd_complete(cmd->sk, index, MGMT_OP_SET_LOCAL_NAME, &ev, | 2003 | err = cmd_complete(cmd->sk, index, MGMT_OP_SET_LOCAL_NAME, &ev, |
1841 | sizeof(ev)); | 2004 | sizeof(ev)); |
1842 | if (err < 0) | 2005 | if (err < 0) |