diff options
author | Johan Hedberg <johan.hedberg@intel.com> | 2012-02-21 09:01:30 -0500 |
---|---|---|
committer | Johan Hedberg <johan.hedberg@intel.com> | 2012-02-21 13:04:39 -0500 |
commit | 5e5282bbfde9ca6157dba913d90cbab859a837e2 (patch) | |
tree | 202265653320550db8c7aea3087bb5cfc8ca032e /net/bluetooth/mgmt.c | |
parent | 0cbf4ed6e6f43ac399afefdd14a1ee86db8de7d0 (diff) |
Bluetooth: mgmt: Allow connectable/discoverable changes in off state
This patch makes it possible to toggle the connectable & discoverable
settings when powered off. Two new hdev->dev_flags flags are added to
track what the scan mode should be when the device is finally powered
on.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
Acked-by: Marcel Holtmann <marcel@holtmann.org>
Diffstat (limited to 'net/bluetooth/mgmt.c')
-rw-r--r-- | net/bluetooth/mgmt.c | 91 |
1 files changed, 73 insertions, 18 deletions
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 0f87030f9c30..6311be775ff2 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c | |||
@@ -398,10 +398,10 @@ static u32 get_current_settings(struct hci_dev *hdev) | |||
398 | if (!test_bit(HCI_AUTO_OFF, &hdev->dev_flags)) | 398 | if (!test_bit(HCI_AUTO_OFF, &hdev->dev_flags)) |
399 | settings |= MGMT_SETTING_POWERED; | 399 | settings |= MGMT_SETTING_POWERED; |
400 | 400 | ||
401 | if (test_bit(HCI_PSCAN, &hdev->flags)) | 401 | if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) |
402 | settings |= MGMT_SETTING_CONNECTABLE; | 402 | settings |= MGMT_SETTING_CONNECTABLE; |
403 | 403 | ||
404 | if (test_bit(HCI_ISCAN, &hdev->flags)) | 404 | if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) |
405 | settings |= MGMT_SETTING_DISCOVERABLE; | 405 | settings |= MGMT_SETTING_DISCOVERABLE; |
406 | 406 | ||
407 | if (test_bit(HCI_PAIRABLE, &hdev->dev_flags)) | 407 | if (test_bit(HCI_PAIRABLE, &hdev->dev_flags)) |
@@ -804,6 +804,7 @@ static int set_discoverable(struct sock *sk, u16 index, void *data, u16 len) | |||
804 | struct mgmt_cp_set_discoverable *cp = data; | 804 | struct mgmt_cp_set_discoverable *cp = data; |
805 | struct hci_dev *hdev; | 805 | struct hci_dev *hdev; |
806 | struct pending_cmd *cmd; | 806 | struct pending_cmd *cmd; |
807 | u16 timeout; | ||
807 | u8 scan; | 808 | u8 scan; |
808 | int err; | 809 | int err; |
809 | 810 | ||
@@ -818,9 +819,11 @@ static int set_discoverable(struct sock *sk, u16 index, void *data, u16 len) | |||
818 | return cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, | 819 | return cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, |
819 | MGMT_STATUS_INVALID_PARAMS); | 820 | MGMT_STATUS_INVALID_PARAMS); |
820 | 821 | ||
822 | timeout = get_unaligned_le16(&cp->timeout); | ||
823 | |||
821 | hci_dev_lock(hdev); | 824 | hci_dev_lock(hdev); |
822 | 825 | ||
823 | if (!hdev_is_powered(hdev)) { | 826 | if (!hdev_is_powered(hdev) && timeout > 0) { |
824 | err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, | 827 | err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, |
825 | MGMT_STATUS_NOT_POWERED); | 828 | MGMT_STATUS_NOT_POWERED); |
826 | goto failed; | 829 | goto failed; |
@@ -833,8 +836,22 @@ static int set_discoverable(struct sock *sk, u16 index, void *data, u16 len) | |||
833 | goto failed; | 836 | goto failed; |
834 | } | 837 | } |
835 | 838 | ||
836 | if (cp->val == test_bit(HCI_ISCAN, &hdev->flags) && | 839 | if (!test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) { |
837 | test_bit(HCI_PSCAN, &hdev->flags)) { | 840 | err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, |
841 | MGMT_STATUS_REJECTED); | ||
842 | goto failed; | ||
843 | } | ||
844 | |||
845 | if (!hdev_is_powered(hdev)) { | ||
846 | if (cp->val) | ||
847 | set_bit(HCI_DISCOVERABLE, &hdev->dev_flags); | ||
848 | else | ||
849 | clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags); | ||
850 | err = send_settings_rsp(sk, MGMT_OP_SET_DISCOVERABLE, hdev); | ||
851 | goto failed; | ||
852 | } | ||
853 | |||
854 | if (!!cp->val == test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) { | ||
838 | err = send_settings_rsp(sk, MGMT_OP_SET_DISCOVERABLE, hdev); | 855 | err = send_settings_rsp(sk, MGMT_OP_SET_DISCOVERABLE, hdev); |
839 | goto failed; | 856 | goto failed; |
840 | } | 857 | } |
@@ -857,7 +874,7 @@ static int set_discoverable(struct sock *sk, u16 index, void *data, u16 len) | |||
857 | mgmt_pending_remove(cmd); | 874 | mgmt_pending_remove(cmd); |
858 | 875 | ||
859 | if (cp->val) | 876 | if (cp->val) |
860 | hdev->discov_timeout = get_unaligned_le16(&cp->timeout); | 877 | hdev->discov_timeout = timeout; |
861 | 878 | ||
862 | failed: | 879 | failed: |
863 | hci_dev_unlock(hdev); | 880 | hci_dev_unlock(hdev); |
@@ -888,8 +905,13 @@ static int set_connectable(struct sock *sk, u16 index, void *data, u16 len) | |||
888 | hci_dev_lock(hdev); | 905 | hci_dev_lock(hdev); |
889 | 906 | ||
890 | if (!hdev_is_powered(hdev)) { | 907 | if (!hdev_is_powered(hdev)) { |
891 | err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, | 908 | if (cp->val) |
892 | MGMT_STATUS_NOT_POWERED); | 909 | set_bit(HCI_CONNECTABLE, &hdev->dev_flags); |
910 | else { | ||
911 | clear_bit(HCI_CONNECTABLE, &hdev->dev_flags); | ||
912 | clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags); | ||
913 | } | ||
914 | err = send_settings_rsp(sk, MGMT_OP_SET_CONNECTABLE, hdev); | ||
893 | goto failed; | 915 | goto failed; |
894 | } | 916 | } |
895 | 917 | ||
@@ -900,7 +922,7 @@ static int set_connectable(struct sock *sk, u16 index, void *data, u16 len) | |||
900 | goto failed; | 922 | goto failed; |
901 | } | 923 | } |
902 | 924 | ||
903 | if (cp->val == test_bit(HCI_PSCAN, &hdev->flags)) { | 925 | if (!!cp->val == test_bit(HCI_PSCAN, &hdev->flags)) { |
904 | err = send_settings_rsp(sk, MGMT_OP_SET_CONNECTABLE, hdev); | 926 | err = send_settings_rsp(sk, MGMT_OP_SET_CONNECTABLE, hdev); |
905 | goto failed; | 927 | goto failed; |
906 | } | 928 | } |
@@ -2881,9 +2903,22 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered) | |||
2881 | __le32 ev; | 2903 | __le32 ev; |
2882 | int err; | 2904 | int err; |
2883 | 2905 | ||
2906 | if (!test_bit(HCI_MGMT, &hdev->dev_flags)) | ||
2907 | return 0; | ||
2908 | |||
2884 | mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match); | 2909 | mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match); |
2885 | 2910 | ||
2886 | if (!powered) { | 2911 | if (powered) { |
2912 | u8 scan = 0; | ||
2913 | |||
2914 | if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) | ||
2915 | scan |= SCAN_PAGE; | ||
2916 | if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) | ||
2917 | scan |= SCAN_INQUIRY; | ||
2918 | |||
2919 | if (scan) | ||
2920 | hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); | ||
2921 | } else { | ||
2887 | u8 status = ENETDOWN; | 2922 | u8 status = ENETDOWN; |
2888 | mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status); | 2923 | mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status); |
2889 | } | 2924 | } |
@@ -2902,15 +2937,25 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered) | |||
2902 | int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable) | 2937 | int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable) |
2903 | { | 2938 | { |
2904 | struct cmd_lookup match = { NULL, hdev }; | 2939 | struct cmd_lookup match = { NULL, hdev }; |
2905 | __le32 ev; | 2940 | bool changed = false; |
2906 | int err; | 2941 | int err = 0; |
2907 | 2942 | ||
2908 | mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev, settings_rsp, &match); | 2943 | mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev, settings_rsp, &match); |
2909 | 2944 | ||
2910 | ev = cpu_to_le32(get_current_settings(hdev)); | 2945 | if (discoverable) { |
2946 | if (!test_and_set_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) | ||
2947 | changed = true; | ||
2948 | } else { | ||
2949 | if (test_and_clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) | ||
2950 | changed = true; | ||
2951 | } | ||
2911 | 2952 | ||
2912 | err = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), | 2953 | if (changed) { |
2954 | __le32 ev = cpu_to_le32(get_current_settings(hdev)); | ||
2955 | err = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), | ||
2913 | match.sk); | 2956 | match.sk); |
2957 | } | ||
2958 | |||
2914 | if (match.sk) | 2959 | if (match.sk) |
2915 | sock_put(match.sk); | 2960 | sock_put(match.sk); |
2916 | 2961 | ||
@@ -2919,16 +2964,26 @@ int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable) | |||
2919 | 2964 | ||
2920 | int mgmt_connectable(struct hci_dev *hdev, u8 connectable) | 2965 | int mgmt_connectable(struct hci_dev *hdev, u8 connectable) |
2921 | { | 2966 | { |
2922 | __le32 ev; | ||
2923 | struct cmd_lookup match = { NULL, hdev }; | 2967 | struct cmd_lookup match = { NULL, hdev }; |
2924 | int err; | 2968 | bool changed = false; |
2969 | int err = 0; | ||
2925 | 2970 | ||
2926 | mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev, settings_rsp, | 2971 | mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev, settings_rsp, |
2927 | &match); | 2972 | &match); |
2928 | 2973 | ||
2929 | ev = cpu_to_le32(get_current_settings(hdev)); | 2974 | if (connectable) { |
2975 | if (!test_and_set_bit(HCI_CONNECTABLE, &hdev->dev_flags)) | ||
2976 | changed = true; | ||
2977 | } else { | ||
2978 | if (test_and_clear_bit(HCI_CONNECTABLE, &hdev->dev_flags)) | ||
2979 | changed = true; | ||
2980 | } | ||
2930 | 2981 | ||
2931 | err = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), match.sk); | 2982 | if (changed) { |
2983 | __le32 ev = cpu_to_le32(get_current_settings(hdev)); | ||
2984 | err = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), | ||
2985 | match.sk); | ||
2986 | } | ||
2932 | 2987 | ||
2933 | if (match.sk) | 2988 | if (match.sk) |
2934 | sock_put(match.sk); | 2989 | sock_put(match.sk); |