[Bluetooth] Add automatic sniff mode support
Marcel Holtmann [Mon, 3 Jul 2006 08:02:33 +0000 (10:02 +0200)]
This patch introduces the automatic sniff mode feature. This allows
the host to switch idle connections into sniff mode to safe power.

Signed-off-by: Ulisses Furquim <ulissesf@gmail.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>

include/net/bluetooth/hci.h
include/net/bluetooth/hci_core.h
net/bluetooth/af_bluetooth.c
net/bluetooth/hci_conn.c
net/bluetooth/hci_core.c
net/bluetooth/hci_event.c
net/bluetooth/hci_sysfs.c

index 99c53f6..b2bdb1a 100644 (file)
@@ -101,9 +101,10 @@ enum {
 #define HCIINQUIRY     _IOR('H', 240, int)
 
 /* HCI timeouts */
-#define HCI_CONN_TIMEOUT       (HZ * 40)
-#define HCI_DISCONN_TIMEOUT    (HZ * 2)
-#define HCI_CONN_IDLE_TIMEOUT  (HZ * 60)
+#define HCI_CONNECT_TIMEOUT    (40000) /* 40 seconds */
+#define HCI_DISCONN_TIMEOUT    (2000)  /* 2 seconds */
+#define HCI_IDLE_TIMEOUT       (6000)  /* 6 seconds */
+#define HCI_INIT_TIMEOUT       (10000) /* 10 seconds */
 
 /* HCI Packet types */
 #define HCI_COMMAND_PKT                0x01
@@ -145,7 +146,7 @@ enum {
 #define LMP_TACCURACY  0x10
 #define LMP_RSWITCH    0x20
 #define LMP_HOLD       0x40
-#define LMP_SNIF       0x80
+#define LMP_SNIFF      0x80
 
 #define LMP_PARK       0x01
 #define LMP_RSSI       0x02
@@ -160,13 +161,21 @@ enum {
 #define LMP_PSCHEME    0x02
 #define LMP_PCONTROL   0x04
 
+#define LMP_SNIFF_SUBR 0x02
+
+/* Connection modes */
+#define HCI_CM_ACTIVE  0x0000
+#define HCI_CM_HOLD    0x0001
+#define HCI_CM_SNIFF   0x0002
+#define HCI_CM_PARK    0x0003
+
 /* Link policies */
 #define HCI_LP_RSWITCH 0x0001
 #define HCI_LP_HOLD    0x0002
 #define HCI_LP_SNIFF   0x0004
 #define HCI_LP_PARK    0x0008
 
-/* Link mode */
+/* Link modes */
 #define HCI_LM_ACCEPT  0x8000
 #define HCI_LM_MASTER  0x0001
 #define HCI_LM_AUTH    0x0002
@@ -192,7 +201,7 @@ struct hci_rp_read_loc_version {
 } __attribute__ ((packed));
 
 #define OCF_READ_LOCAL_FEATURES        0x0003
-struct hci_rp_read_loc_features {
+struct hci_rp_read_local_features {
        __u8 status;
        __u8 features[8];
 } __attribute__ ((packed));
@@ -376,17 +385,32 @@ struct hci_cp_change_conn_link_key {
 } __attribute__ ((packed));
 
 #define OCF_READ_REMOTE_FEATURES 0x001B
-struct hci_cp_read_rmt_features {
+struct hci_cp_read_remote_features {
        __le16   handle;
 } __attribute__ ((packed));
 
 #define OCF_READ_REMOTE_VERSION 0x001D
-struct hci_cp_read_rmt_version {
+struct hci_cp_read_remote_version {
        __le16   handle;
 } __attribute__ ((packed));
 
 /* Link Policy */
-#define OGF_LINK_POLICY         0x02   
+#define OGF_LINK_POLICY        0x02   
+
+#define OCF_SNIFF_MODE         0x0003
+struct hci_cp_sniff_mode {
+       __le16   handle;
+       __le16   max_interval;
+       __le16   min_interval;
+       __le16   attempt;
+       __le16   timeout;
+} __attribute__ ((packed));
+
+#define OCF_EXIT_SNIFF_MODE    0x0004
+struct hci_cp_exit_sniff_mode {
+       __le16   handle;
+} __attribute__ ((packed));
+
 #define OCF_ROLE_DISCOVERY     0x0009
 struct hci_cp_role_discovery {
        __le16   handle;
@@ -407,7 +431,7 @@ struct hci_rp_read_link_policy {
        __le16   policy;
 } __attribute__ ((packed));
 
-#define OCF_SWITCH_ROLE        0x000B
+#define OCF_SWITCH_ROLE                0x000B
 struct hci_cp_switch_role {
        bdaddr_t bdaddr;
        __u8     role;
@@ -423,6 +447,14 @@ struct hci_rp_write_link_policy {
        __le16   handle;
 } __attribute__ ((packed));
 
+#define OCF_SNIFF_SUBRATE      0x0011
+struct hci_cp_sniff_subrate {
+       __le16   handle;
+       __le16   max_latency;
+       __le16   min_remote_timeout;
+       __le16   min_local_timeout;
+} __attribute__ ((packed));
+
 /* Status params */
 #define OGF_STATUS_PARAM       0x05
 
@@ -582,15 +614,15 @@ struct hci_ev_link_key_notify {
        __u8     key_type;
 } __attribute__ ((packed));
 
-#define HCI_EV_RMT_FEATURES    0x0B
-struct hci_ev_rmt_features {
+#define HCI_EV_REMOTE_FEATURES 0x0B
+struct hci_ev_remote_features {
        __u8     status;
        __le16   handle;
        __u8     features[8];
 } __attribute__ ((packed));
 
-#define HCI_EV_RMT_VERSION     0x0C
-struct hci_ev_rmt_version {
+#define HCI_EV_REMOTE_VERSION  0x0C
+struct hci_ev_remote_version {
        __u8     status;
        __le16   handle;
        __u8     lmp_ver;
@@ -611,6 +643,16 @@ struct hci_ev_pscan_rep_mode {
        __u8     pscan_rep_mode;
 } __attribute__ ((packed));
 
+#define HCI_EV_SNIFF_SUBRATE   0x2E
+struct hci_ev_sniff_subrate {
+       __u8     status;
+       __le16   handle;
+       __le16   max_tx_latency;
+       __le16   max_rx_latency;
+       __le16   max_remote_timeout;
+       __le16   max_local_timeout;
+} __attribute__ ((packed));
+
 /* Internal events generated by Bluetooth stack */
 #define HCI_EV_STACK_INTERNAL  0xFD
 struct hci_ev_stack_internal {
index bb9f81d..f685270 100644 (file)
 #define HCI_PROTO_L2CAP        0
 #define HCI_PROTO_SCO  1
 
-#define HCI_INIT_TIMEOUT (HZ * 10)
-
 /* HCI Core structures */
-
 struct inquiry_data {
        bdaddr_t        bdaddr;
        __u8            pscan_rep_mode;
@@ -81,6 +78,10 @@ struct hci_dev {
        __u16           link_policy;
        __u16           link_mode;
 
+       __u32           idle_timeout;
+       __u16           sniff_min_interval;
+       __u16           sniff_max_interval;
+
        unsigned long   quirks;
 
        atomic_t        cmd_cnt;
@@ -145,18 +146,24 @@ struct hci_conn {
        bdaddr_t         dst;
        __u16            handle;
        __u16            state;
+       __u8             mode;
        __u8             type;
        __u8             out;
        __u8             dev_class[3];
+       __u8             features[8];
+       __u16            interval;
+       __u16            link_policy;
        __u32            link_mode;
+       __u8             power_save;
        unsigned long    pend;
-       
+
        unsigned int     sent;
-       
+
        struct sk_buff_head data_q;
 
-       struct timer_list timer;
-       
+       struct timer_list disc_timer;
+       struct timer_list idle_timer;
+
        struct hci_dev  *hdev;
        void            *l2cap_data;
        void            *sco_data;
@@ -211,7 +218,8 @@ void hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data);
 enum {
        HCI_CONN_AUTH_PEND,
        HCI_CONN_ENCRYPT_PEND,
-       HCI_CONN_RSWITCH_PEND
+       HCI_CONN_RSWITCH_PEND,
+       HCI_CONN_MODE_CHANGE_PEND,
 };
 
 static inline void hci_conn_hash_init(struct hci_dev *hdev)
@@ -286,31 +294,27 @@ int hci_conn_encrypt(struct hci_conn *conn);
 int hci_conn_change_link_key(struct hci_conn *conn);
 int hci_conn_switch_role(struct hci_conn *conn, uint8_t role);
 
-static inline void hci_conn_set_timer(struct hci_conn *conn, unsigned long timeout)
-{
-       mod_timer(&conn->timer, jiffies + timeout);
-}
-
-static inline void hci_conn_del_timer(struct hci_conn *conn)
-{
-       del_timer(&conn->timer);
-}
+void hci_conn_enter_active_mode(struct hci_conn *conn);
+void hci_conn_enter_sniff_mode(struct hci_conn *conn);
 
 static inline void hci_conn_hold(struct hci_conn *conn)
 {
        atomic_inc(&conn->refcnt);
-       hci_conn_del_timer(conn);
+       del_timer(&conn->disc_timer);
 }
 
 static inline void hci_conn_put(struct hci_conn *conn)
 {
        if (atomic_dec_and_test(&conn->refcnt)) {
+               unsigned long timeo;
                if (conn->type == ACL_LINK) {
-                       unsigned long timeo = (conn->out) ?
-                               HCI_DISCONN_TIMEOUT : HCI_DISCONN_TIMEOUT * 2;
-                       hci_conn_set_timer(conn, timeo);
+                       timeo = msecs_to_jiffies(HCI_DISCONN_TIMEOUT);
+                       if (!conn->out)
+                               timeo *= 2;
+                       del_timer(&conn->idle_timer);
                } else
-                       hci_conn_set_timer(conn, HZ / 100);
+                       timeo = msecs_to_jiffies(10);
+               mod_timer(&conn->disc_timer, jiffies + timeo);
        }
 }
 
@@ -411,8 +415,10 @@ void hci_unregister_sysfs(struct hci_dev *hdev);
 #define SET_HCIDEV_DEV(hdev, pdev) ((hdev)->class_dev.dev = (pdev))
 
 /* ----- LMP capabilities ----- */
-#define lmp_rswitch_capable(dev) (dev->features[0] & LMP_RSWITCH)
-#define lmp_encrypt_capable(dev) (dev->features[0] & LMP_ENCRYPT)
+#define lmp_rswitch_capable(dev)   ((dev)->features[0] & LMP_RSWITCH)
+#define lmp_encrypt_capable(dev)   ((dev)->features[0] & LMP_ENCRYPT)
+#define lmp_sniff_capable(dev)     ((dev)->features[0] & LMP_SNIFF)
+#define lmp_sniffsubr_capable(dev) ((dev)->features[5] & LMP_SNIFF_SUBR)
 
 /* ----- HCI protocols ----- */
 struct hci_proto {
index 51f8670..729461f 100644 (file)
@@ -48,7 +48,7 @@
 #define BT_DBG(D...)
 #endif
 
-#define VERSION "2.8"
+#define VERSION "2.9"
 
 /* Bluetooth sockets */
 #define BT_MAX_PROTO   8
index 5c0c2b1..420ed4d 100644 (file)
@@ -115,8 +115,8 @@ void hci_add_sco(struct hci_conn *conn, __u16 handle)
 
 static void hci_conn_timeout(unsigned long arg)
 {
-       struct hci_conn *conn = (void *)arg;
-       struct hci_dev  *hdev = conn->hdev;
+       struct hci_conn *conn = (void *) arg;
+       struct hci_dev *hdev = conn->hdev;
 
        BT_DBG("conn %p state %d", conn, conn->state);
 
@@ -132,11 +132,13 @@ static void hci_conn_timeout(unsigned long arg)
        return;
 }
 
-static void hci_conn_init_timer(struct hci_conn *conn)
+static void hci_conn_idle(unsigned long arg)
 {
-       init_timer(&conn->timer);
-       conn->timer.function = hci_conn_timeout;
-       conn->timer.data = (unsigned long)conn;
+       struct hci_conn *conn = (void *) arg;
+
+       BT_DBG("conn %p mode %d", conn, conn->mode);
+
+       hci_conn_enter_sniff_mode(conn);
 }
 
 struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
@@ -145,17 +147,27 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
 
        BT_DBG("%s dst %s", hdev->name, batostr(dst));
 
-       if (!(conn = kmalloc(sizeof(struct hci_conn), GFP_ATOMIC)))
+       conn = kzalloc(sizeof(struct hci_conn), GFP_ATOMIC);
+       if (!conn)
                return NULL;
-       memset(conn, 0, sizeof(struct hci_conn));
 
        bacpy(&conn->dst, dst);
-       conn->type   = type;
        conn->hdev   = hdev;
+       conn->type   = type;
+       conn->mode   = HCI_CM_ACTIVE;
        conn->state  = BT_OPEN;
 
+       conn->power_save = 1;
+
        skb_queue_head_init(&conn->data_q);
-       hci_conn_init_timer(conn);
+
+       init_timer(&conn->disc_timer);
+       conn->disc_timer.function = hci_conn_timeout;
+       conn->disc_timer.data = (unsigned long) conn;
+
+       init_timer(&conn->idle_timer);
+       conn->idle_timer.function = hci_conn_idle;
+       conn->idle_timer.data = (unsigned long) conn;
 
        atomic_set(&conn->refcnt, 0);
 
@@ -178,7 +190,9 @@ int hci_conn_del(struct hci_conn *conn)
 
        BT_DBG("%s conn %p handle %d", hdev->name, conn, conn->handle);
 
-       hci_conn_del_timer(conn);
+       del_timer(&conn->idle_timer);
+
+       del_timer(&conn->disc_timer);
 
        if (conn->type == SCO_LINK) {
                struct hci_conn *acl = conn->link;
@@ -364,6 +378,70 @@ int hci_conn_switch_role(struct hci_conn *conn, uint8_t role)
 }
 EXPORT_SYMBOL(hci_conn_switch_role);
 
+/* Enter active mode */
+void hci_conn_enter_active_mode(struct hci_conn *conn)
+{
+       struct hci_dev *hdev = conn->hdev;
+
+       BT_DBG("conn %p mode %d", conn, conn->mode);
+
+       if (test_bit(HCI_RAW, &hdev->flags))
+               return;
+
+       if (conn->mode != HCI_CM_SNIFF || !conn->power_save)
+               goto timer;
+
+       if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend)) {
+               struct hci_cp_exit_sniff_mode cp;
+               cp.handle = __cpu_to_le16(conn->handle);
+               hci_send_cmd(hdev, OGF_LINK_POLICY,
+                               OCF_EXIT_SNIFF_MODE, sizeof(cp), &cp);
+       }
+
+timer:
+       if (hdev->idle_timeout > 0)
+               mod_timer(&conn->idle_timer,
+                       jiffies + msecs_to_jiffies(hdev->idle_timeout));
+}
+
+/* Enter sniff mode */
+void hci_conn_enter_sniff_mode(struct hci_conn *conn)
+{
+       struct hci_dev *hdev = conn->hdev;
+
+       BT_DBG("conn %p mode %d", conn, conn->mode);
+
+       if (test_bit(HCI_RAW, &hdev->flags))
+               return;
+
+       if (!lmp_sniff_capable(hdev) || !lmp_sniff_capable(conn))
+               return;
+
+       if (conn->mode != HCI_CM_ACTIVE || !(conn->link_policy & HCI_LP_SNIFF))
+               return;
+
+       if (lmp_sniffsubr_capable(hdev) && lmp_sniffsubr_capable(conn)) {
+               struct hci_cp_sniff_subrate cp;
+               cp.handle             = __cpu_to_le16(conn->handle);
+               cp.max_latency        = __constant_cpu_to_le16(0);
+               cp.min_remote_timeout = __constant_cpu_to_le16(0);
+               cp.min_local_timeout  = __constant_cpu_to_le16(0);
+               hci_send_cmd(hdev, OGF_LINK_POLICY,
+                               OCF_SNIFF_SUBRATE, sizeof(cp), &cp);
+       }
+
+       if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend)) {
+               struct hci_cp_sniff_mode cp;
+               cp.handle       = __cpu_to_le16(conn->handle);
+               cp.max_interval = __cpu_to_le16(hdev->sniff_max_interval);
+               cp.min_interval = __cpu_to_le16(hdev->sniff_min_interval);
+               cp.attempt      = __constant_cpu_to_le16(4);
+               cp.timeout      = __constant_cpu_to_le16(1);
+               hci_send_cmd(hdev, OGF_LINK_POLICY,
+                               OCF_SNIFF_MODE, sizeof(cp), &cp);
+       }
+}
+
 /* Drop all connection on the device */
 void hci_conn_hash_flush(struct hci_dev *hdev)
 {
index f67240b..3f9f156 100644 (file)
@@ -411,7 +411,7 @@ int hci_inquiry(void __user *arg)
        }
        hci_dev_unlock_bh(hdev);
 
-       timeo = ir.length * 2 * HZ;
+       timeo = ir.length * msecs_to_jiffies(2000);
        if (do_inquiry && (err = hci_request(hdev, hci_inq_req, (unsigned long)&ir, timeo)) < 0)
                goto done;
 
@@ -479,7 +479,8 @@ int hci_dev_open(__u16 dev)
                set_bit(HCI_INIT, &hdev->flags);
 
                //__hci_request(hdev, hci_reset_req, 0, HZ);
-               ret = __hci_request(hdev, hci_init_req, 0, HCI_INIT_TIMEOUT);
+               ret = __hci_request(hdev, hci_init_req, 0,
+                                       msecs_to_jiffies(HCI_INIT_TIMEOUT));
 
                clear_bit(HCI_INIT, &hdev->flags);
        }
@@ -546,7 +547,8 @@ static int hci_dev_do_close(struct hci_dev *hdev)
        atomic_set(&hdev->cmd_cnt, 1);
        if (!test_bit(HCI_RAW, &hdev->flags)) {
                set_bit(HCI_INIT, &hdev->flags);
-               __hci_request(hdev, hci_reset_req, 0, HZ/4);
+               __hci_request(hdev, hci_reset_req, 0,
+                                       msecs_to_jiffies(250));
                clear_bit(HCI_INIT, &hdev->flags);
        }
 
@@ -619,7 +621,8 @@ int hci_dev_reset(__u16 dev)
        hdev->acl_cnt = 0; hdev->sco_cnt = 0;
 
        if (!test_bit(HCI_RAW, &hdev->flags))
-               ret = __hci_request(hdev, hci_reset_req, 0, HCI_INIT_TIMEOUT);
+               ret = __hci_request(hdev, hci_reset_req, 0,
+                                       msecs_to_jiffies(HCI_INIT_TIMEOUT));
 
 done:
        tasklet_enable(&hdev->tx_task);
@@ -657,7 +660,8 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg)
 
        switch (cmd) {
        case HCISETAUTH:
-               err = hci_request(hdev, hci_auth_req, dr.dev_opt, HCI_INIT_TIMEOUT);
+               err = hci_request(hdev, hci_auth_req, dr.dev_opt,
+                                       msecs_to_jiffies(HCI_INIT_TIMEOUT));
                break;
 
        case HCISETENCRYPT:
@@ -668,18 +672,19 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg)
 
                if (!test_bit(HCI_AUTH, &hdev->flags)) {
                        /* Auth must be enabled first */
-                       err = hci_request(hdev, hci_auth_req,
-                                       dr.dev_opt, HCI_INIT_TIMEOUT);
+                       err = hci_request(hdev, hci_auth_req, dr.dev_opt,
+                                       msecs_to_jiffies(HCI_INIT_TIMEOUT));
                        if (err)
                                break;
                }
 
-               err = hci_request(hdev, hci_encrypt_req,
-                                       dr.dev_opt, HCI_INIT_TIMEOUT);
+               err = hci_request(hdev, hci_encrypt_req, dr.dev_opt,
+                                       msecs_to_jiffies(HCI_INIT_TIMEOUT));
                break;
 
        case HCISETSCAN:
-               err = hci_request(hdev, hci_scan_req, dr.dev_opt, HCI_INIT_TIMEOUT);
+               err = hci_request(hdev, hci_scan_req, dr.dev_opt,
+                                       msecs_to_jiffies(HCI_INIT_TIMEOUT));
                break;
 
        case HCISETPTYPE:
@@ -848,6 +853,10 @@ int hci_register_dev(struct hci_dev *hdev)
        hdev->pkt_type  = (HCI_DM1 | HCI_DH1 | HCI_HV1);
        hdev->link_mode = (HCI_LM_ACCEPT);
 
+       hdev->idle_timeout = 0;
+       hdev->sniff_max_interval = 800;
+       hdev->sniff_min_interval = 80;
+
        tasklet_init(&hdev->cmd_task, hci_cmd_task,(unsigned long) hdev);
        tasklet_init(&hdev->rx_task, hci_rx_task, (unsigned long) hdev);
        tasklet_init(&hdev->tx_task, hci_tx_task, (unsigned long) hdev);
@@ -1220,6 +1229,9 @@ static inline void hci_sched_acl(struct hci_dev *hdev)
        while (hdev->acl_cnt && (conn = hci_low_sent(hdev, ACL_LINK, &quote))) {
                while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
                        BT_DBG("skb %p len %d", skb, skb->len);
+
+                       hci_conn_enter_active_mode(conn);
+
                        hci_send_frame(skb);
                        hdev->acl_last_tx = jiffies;
 
@@ -1298,6 +1310,8 @@ static inline void hci_acldata_packet(struct hci_dev *hdev, struct sk_buff *skb)
        if (conn) {
                register struct hci_proto *hp;
 
+               hci_conn_enter_active_mode(conn);
+
                /* Send to upper protocol */
                if ((hp = hci_proto[HCI_PROTO_L2CAP]) && hp->recv_acldata) {
                        hp->recv_acldata(conn, skb, flags);
index f41cf1a..3896dab 100644 (file)
@@ -83,6 +83,8 @@ static void hci_cc_link_policy(struct hci_dev *hdev, __u16 ocf, struct sk_buff *
 {
        struct hci_conn *conn;
        struct hci_rp_role_discovery *rd;
+       struct hci_rp_write_link_policy *lp;
+       void *sent;
 
        BT_DBG("%s ocf 0x%x", hdev->name, ocf);
 
@@ -106,6 +108,27 @@ static void hci_cc_link_policy(struct hci_dev *hdev, __u16 ocf, struct sk_buff *
                hci_dev_unlock(hdev);
                break;
 
+       case OCF_WRITE_LINK_POLICY:
+               sent = hci_sent_cmd_data(hdev, OGF_LINK_POLICY, OCF_WRITE_LINK_POLICY);
+               if (!sent)
+                       break;
+
+               lp = (struct hci_rp_write_link_policy *) skb->data;
+
+               if (lp->status)
+                       break;
+
+               hci_dev_lock(hdev);
+
+               conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(lp->handle));
+               if (conn) {
+                       __le16 policy = get_unaligned((__le16 *) (sent + 2));
+                       conn->link_policy = __le16_to_cpu(policy);
+               }
+
+               hci_dev_unlock(hdev);
+               break;
+
        default:
                BT_DBG("%s: Command complete: ogf LINK_POLICY ocf %x", 
                                hdev->name, ocf);
@@ -274,7 +297,7 @@ static void hci_cc_host_ctl(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb
 /* Command Complete OGF INFO_PARAM  */
 static void hci_cc_info_param(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb)
 {
-       struct hci_rp_read_loc_features *lf;
+       struct hci_rp_read_local_features *lf;
        struct hci_rp_read_buffer_size *bs;
        struct hci_rp_read_bd_addr *ba;
 
@@ -282,7 +305,7 @@ static void hci_cc_info_param(struct hci_dev *hdev, __u16 ocf, struct sk_buff *s
 
        switch (ocf) {
        case OCF_READ_LOCAL_FEATURES:
-               lf = (struct hci_rp_read_loc_features *) skb->data;
+               lf = (struct hci_rp_read_local_features *) skb->data;
 
                if (lf->status) {
                        BT_DBG("%s READ_LOCAL_FEATURES failed %d", hdev->name, lf->status);
@@ -447,8 +470,46 @@ static void hci_cs_link_policy(struct hci_dev *hdev, __u16 ocf, __u8 status)
        BT_DBG("%s ocf 0x%x", hdev->name, ocf);
 
        switch (ocf) {
+       case OCF_SNIFF_MODE:
+               if (status) {
+                       struct hci_conn *conn;
+                       struct hci_cp_sniff_mode *cp = hci_sent_cmd_data(hdev, OGF_LINK_POLICY, OCF_SNIFF_MODE);
+
+                       if (!cp)
+                               break;
+
+                       hci_dev_lock(hdev);
+
+                       conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
+                       if (conn) {
+                               clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend);
+                       }
+
+                       hci_dev_unlock(hdev);
+               }
+               break;
+
+       case OCF_EXIT_SNIFF_MODE:
+               if (status) {
+                       struct hci_conn *conn;
+                       struct hci_cp_exit_sniff_mode *cp = hci_sent_cmd_data(hdev, OGF_LINK_POLICY, OCF_EXIT_SNIFF_MODE);
+
+                       if (!cp)
+                               break;
+
+                       hci_dev_lock(hdev);
+
+                       conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
+                       if (conn) {
+                               clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend);
+                       }
+
+                       hci_dev_unlock(hdev);
+               }
+               break;
+
        default:
-               BT_DBG("%s Command status: ogf HOST_POLICY ocf %x", hdev->name, ocf);
+               BT_DBG("%s Command status: ogf LINK_POLICY ocf %x", hdev->name, ocf);
                break;
        }
 }
@@ -630,14 +691,16 @@ static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *sk
                else
                        cp.role = 0x01; /* Remain slave */
 
-               hci_send_cmd(hdev, OGF_LINK_CTL, OCF_ACCEPT_CONN_REQ, sizeof(cp), &cp);
+               hci_send_cmd(hdev, OGF_LINK_CTL,
+                               OCF_ACCEPT_CONN_REQ, sizeof(cp), &cp);
        } else {
                /* Connection rejected */
                struct hci_cp_reject_conn_req cp;
 
                bacpy(&cp.bdaddr, &ev->bdaddr);
                cp.reason = 0x0f;
-               hci_send_cmd(hdev, OGF_LINK_CTL, OCF_REJECT_CONN_REQ, sizeof(cp), &cp);
+               hci_send_cmd(hdev, OGF_LINK_CTL,
+                               OCF_REJECT_CONN_REQ, sizeof(cp), &cp);
        }
 }
 
@@ -645,7 +708,7 @@ static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *sk
 static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
 {
        struct hci_ev_conn_complete *ev = (struct hci_ev_conn_complete *) skb->data;
-       struct hci_conn *conn = NULL;
+       struct hci_conn *conn;
 
        BT_DBG("%s", hdev->name);
 
@@ -667,12 +730,21 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
                if (test_bit(HCI_ENCRYPT, &hdev->flags))
                        conn->link_mode |= HCI_LM_ENCRYPT;
 
+               /* Get remote features */
+               if (conn->type == ACL_LINK) {
+                       struct hci_cp_read_remote_features cp;
+                       cp.handle = ev->handle;
+                       hci_send_cmd(hdev, OGF_LINK_CTL,
+                               OCF_READ_REMOTE_FEATURES, sizeof(cp), &cp);
+               }
+
                /* Set link policy */
                if (conn->type == ACL_LINK && hdev->link_policy) {
                        struct hci_cp_write_link_policy cp;
                        cp.handle = ev->handle;
                        cp.policy = __cpu_to_le16(hdev->link_policy);
-                       hci_send_cmd(hdev, OGF_LINK_POLICY, OCF_WRITE_LINK_POLICY, sizeof(cp), &cp);
+                       hci_send_cmd(hdev, OGF_LINK_POLICY,
+                               OCF_WRITE_LINK_POLICY, sizeof(cp), &cp);
                }
 
                /* Set packet type for incoming connection */
@@ -683,7 +755,8 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
                                __cpu_to_le16(hdev->pkt_type & ACL_PTYPE_MASK):
                                __cpu_to_le16(hdev->pkt_type & SCO_PTYPE_MASK);
 
-                       hci_send_cmd(hdev, OGF_LINK_CTL, OCF_CHANGE_CONN_PTYPE, sizeof(cp), &cp);
+                       hci_send_cmd(hdev, OGF_LINK_CTL,
+                               OCF_CHANGE_CONN_PTYPE, sizeof(cp), &cp);
                }
        } else
                conn->state = BT_CLOSED;
@@ -711,8 +784,7 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
 static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
 {
        struct hci_ev_disconn_complete *ev = (struct hci_ev_disconn_complete *) skb->data;
-       struct hci_conn *conn = NULL;
-       __u16 handle = __le16_to_cpu(ev->handle);
+       struct hci_conn *conn;
 
        BT_DBG("%s status %d", hdev->name, ev->status);
 
@@ -721,7 +793,7 @@ static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff
 
        hci_dev_lock(hdev);
 
-       conn = hci_conn_hash_lookup_handle(hdev, handle);
+       conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
        if (conn) {
                conn->state = BT_CLOSED;
                hci_proto_disconn_ind(conn, ev->reason);
@@ -778,7 +850,7 @@ static inline void hci_num_comp_pkts_evt(struct hci_dev *hdev, struct sk_buff *s
 static inline void hci_role_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
 {
        struct hci_ev_role_change *ev = (struct hci_ev_role_change *) skb->data;
-       struct hci_conn *conn = NULL;
+       struct hci_conn *conn;
 
        BT_DBG("%s status %d", hdev->name, ev->status);
 
@@ -801,18 +873,43 @@ static inline void hci_role_change_evt(struct hci_dev *hdev, struct sk_buff *skb
        hci_dev_unlock(hdev);
 }
 
+/* Mode Change */
+static inline void hci_mode_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+       struct hci_ev_mode_change *ev = (struct hci_ev_mode_change *) skb->data;
+       struct hci_conn *conn;
+
+       BT_DBG("%s status %d", hdev->name, ev->status);
+
+       hci_dev_lock(hdev);
+
+       conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
+       if (conn) {
+               conn->mode = ev->mode;
+               conn->interval = __le16_to_cpu(ev->interval);
+
+               if (!test_and_clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend)) {
+                       if (conn->mode == HCI_CM_ACTIVE)
+                               conn->power_save = 1;
+                       else
+                               conn->power_save = 0;
+               }
+       }
+
+       hci_dev_unlock(hdev);
+}
+
 /* Authentication Complete */
 static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
 {
        struct hci_ev_auth_complete *ev = (struct hci_ev_auth_complete *) skb->data;
-       struct hci_conn *conn = NULL;
-       __u16 handle = __le16_to_cpu(ev->handle);
+       struct hci_conn *conn;
 
        BT_DBG("%s status %d", hdev->name, ev->status);
 
        hci_dev_lock(hdev);
 
-       conn = hci_conn_hash_lookup_handle(hdev, handle);
+       conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
        if (conn) {
                if (!ev->status)
                        conn->link_mode |= HCI_LM_AUTH;
@@ -827,8 +924,7 @@ static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *s
                                cp.handle  = __cpu_to_le16(conn->handle);
                                cp.encrypt = 1;
                                hci_send_cmd(conn->hdev, OGF_LINK_CTL,
-                                               OCF_SET_CONN_ENCRYPT,
-                                               sizeof(cp), &cp);
+                                       OCF_SET_CONN_ENCRYPT, sizeof(cp), &cp);
                        } else {
                                clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend);
                                hci_encrypt_cfm(conn, ev->status, 0x00);
@@ -843,14 +939,13 @@ static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *s
 static inline void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
 {
        struct hci_ev_encrypt_change *ev = (struct hci_ev_encrypt_change *) skb->data;
-       struct hci_conn *conn = NULL;
-       __u16 handle = __le16_to_cpu(ev->handle);
+       struct hci_conn *conn;
 
        BT_DBG("%s status %d", hdev->name, ev->status);
 
        hci_dev_lock(hdev);
 
-       conn = hci_conn_hash_lookup_handle(hdev, handle);
+       conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
        if (conn) {
                if (!ev->status) {
                        if (ev->encrypt)
@@ -871,14 +966,13 @@ static inline void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff *
 static inline void hci_change_conn_link_key_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
 {
        struct hci_ev_change_conn_link_key_complete *ev = (struct hci_ev_change_conn_link_key_complete *) skb->data;
-       struct hci_conn *conn = NULL;
-       __u16 handle = __le16_to_cpu(ev->handle);
+       struct hci_conn *conn;
 
        BT_DBG("%s status %d", hdev->name, ev->status);
 
        hci_dev_lock(hdev);
 
-       conn = hci_conn_hash_lookup_handle(hdev, handle);
+       conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
        if (conn) {
                if (!ev->status)
                        conn->link_mode |= HCI_LM_SECURE;
@@ -906,18 +1000,35 @@ static inline void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff
 {
 }
 
+/* Remote Features */
+static inline void hci_remote_features_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+       struct hci_ev_remote_features *ev = (struct hci_ev_remote_features *) skb->data;
+       struct hci_conn *conn;
+
+       BT_DBG("%s status %d", hdev->name, ev->status);
+
+       hci_dev_lock(hdev);
+
+       conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
+       if (conn && !ev->status) {
+               memcpy(conn->features, ev->features, sizeof(conn->features));
+       }
+
+       hci_dev_unlock(hdev);
+}
+
 /* Clock Offset */
 static inline void hci_clock_offset_evt(struct hci_dev *hdev, struct sk_buff *skb)
 {
        struct hci_ev_clock_offset *ev = (struct hci_ev_clock_offset *) skb->data;
-       struct hci_conn *conn = NULL;
-       __u16 handle = __le16_to_cpu(ev->handle);
+       struct hci_conn *conn;
 
        BT_DBG("%s status %d", hdev->name, ev->status);
 
        hci_dev_lock(hdev);
 
-       conn = hci_conn_hash_lookup_handle(hdev, handle);
+       conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
        if (conn && !ev->status) {
                struct inquiry_entry *ie;
 
@@ -948,6 +1059,23 @@ static inline void hci_pscan_rep_mode_evt(struct hci_dev *hdev, struct sk_buff *
        hci_dev_unlock(hdev);
 }
 
+/* Sniff Subrate */
+static inline void hci_sniff_subrate_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+       struct hci_ev_sniff_subrate *ev = (struct hci_ev_sniff_subrate *) skb->data;
+       struct hci_conn *conn;
+
+       BT_DBG("%s status %d", hdev->name, ev->status);
+
+       hci_dev_lock(hdev);
+
+       conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
+       if (conn) {
+       }
+
+       hci_dev_unlock(hdev);
+}
+
 void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
 {
        struct hci_event_hdr *hdr = (struct hci_event_hdr *) skb->data;
@@ -996,6 +1124,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
                hci_role_change_evt(hdev, skb);
                break;
 
+       case HCI_EV_MODE_CHANGE:
+               hci_mode_change_evt(hdev, skb);
+               break;
+
        case HCI_EV_AUTH_COMPLETE:
                hci_auth_complete_evt(hdev, skb);
                break;
@@ -1020,6 +1152,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
                hci_link_key_notify_evt(hdev, skb);
                break;
 
+       case HCI_EV_REMOTE_FEATURES:
+               hci_remote_features_evt(hdev, skb);
+               break;
+
        case HCI_EV_CLOCK_OFFSET:
                hci_clock_offset_evt(hdev, skb);
                break;
@@ -1028,6 +1164,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
                hci_pscan_rep_mode_evt(hdev, skb);
                break;
 
+       case HCI_EV_SNIFF_SUBRATE:
+               hci_sniff_subrate_evt(hdev, skb);
+               break;
+
        case HCI_EV_CMD_STATUS:
                cs = (struct hci_ev_cmd_status *) skb->data;
                skb_pull(skb, sizeof(cs));
index 19b234c..89918d2 100644 (file)
@@ -61,18 +61,106 @@ static ssize_t show_inquiry_cache(struct class_device *cdev, char *buf)
        return n;
 }
 
+static ssize_t show_idle_timeout(struct class_device *cdev, char *buf)
+{
+       struct hci_dev *hdev = class_get_devdata(cdev);
+       return sprintf(buf, "%d\n", hdev->idle_timeout);
+}
+
+static ssize_t store_idle_timeout(struct class_device *cdev, const char *buf, size_t count)
+{
+       struct hci_dev *hdev = class_get_devdata(cdev);
+       char *ptr;
+       __u32 val;
+
+       val = simple_strtoul(buf, &ptr, 10);
+       if (ptr == buf)
+               return -EINVAL;
+
+       if (val != 0 && (val < 500 || val > 3600000))
+               return -EINVAL;
+
+       hdev->idle_timeout = val;
+
+       return count;
+}
+
+static ssize_t show_sniff_max_interval(struct class_device *cdev, char *buf)
+{
+       struct hci_dev *hdev = class_get_devdata(cdev);
+       return sprintf(buf, "%d\n", hdev->sniff_max_interval);
+}
+
+static ssize_t store_sniff_max_interval(struct class_device *cdev, const char *buf, size_t count)
+{
+       struct hci_dev *hdev = class_get_devdata(cdev);
+       char *ptr;
+       __u16 val;
+
+       val = simple_strtoul(buf, &ptr, 10);
+       if (ptr == buf)
+               return -EINVAL;
+
+       if (val < 0x0002 || val > 0xFFFE || val % 2)
+               return -EINVAL;
+
+       if (val < hdev->sniff_min_interval)
+               return -EINVAL;
+
+       hdev->sniff_max_interval = val;
+
+       return count;
+}
+
+static ssize_t show_sniff_min_interval(struct class_device *cdev, char *buf)
+{
+       struct hci_dev *hdev = class_get_devdata(cdev);
+       return sprintf(buf, "%d\n", hdev->sniff_min_interval);
+}
+
+static ssize_t store_sniff_min_interval(struct class_device *cdev, const char *buf, size_t count)
+{
+       struct hci_dev *hdev = class_get_devdata(cdev);
+       char *ptr;
+       __u16 val;
+
+       val = simple_strtoul(buf, &ptr, 10);
+       if (ptr == buf)
+               return -EINVAL;
+
+       if (val < 0x0002 || val > 0xFFFE || val % 2)
+               return -EINVAL;
+
+       if (val > hdev->sniff_max_interval)
+               return -EINVAL;
+
+       hdev->sniff_min_interval = val;
+
+       return count;
+}
+
 static CLASS_DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
 static CLASS_DEVICE_ATTR(type, S_IRUGO, show_type, NULL);
 static CLASS_DEVICE_ATTR(address, S_IRUGO, show_address, NULL);
 static CLASS_DEVICE_ATTR(flags, S_IRUGO, show_flags, NULL);
 static CLASS_DEVICE_ATTR(inquiry_cache, S_IRUGO, show_inquiry_cache, NULL);
 
+static CLASS_DEVICE_ATTR(idle_timeout, S_IRUGO | S_IWUSR,
+                               show_idle_timeout, store_idle_timeout);
+static CLASS_DEVICE_ATTR(sniff_max_interval, S_IRUGO | S_IWUSR,
+                               show_sniff_max_interval, store_sniff_max_interval);
+static CLASS_DEVICE_ATTR(sniff_min_interval, S_IRUGO | S_IWUSR,
+                               show_sniff_min_interval, store_sniff_min_interval);
+
 static struct class_device_attribute *bt_attrs[] = {
        &class_device_attr_name,
        &class_device_attr_type,
        &class_device_attr_address,
        &class_device_attr_flags,
        &class_device_attr_inquiry_cache,
+       &class_device_attr_idle_timeout,
+       &class_device_attr_sniff_max_interval,
+       &class_device_attr_sniff_min_interval,
        NULL
 };