Commit 04837f64 authored by Marcel Holtmann's avatar Marcel Holtmann Committed by David S. Miller

[Bluetooth] Add automatic sniff mode support

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: default avatarUlisses Furquim <ulissesf@gmail.com>
Signed-off-by: default avatarMarcel Holtmann <marcel@holtmann.org>
parent da1f5198
...@@ -101,9 +101,10 @@ enum { ...@@ -101,9 +101,10 @@ enum {
#define HCIINQUIRY _IOR('H', 240, int) #define HCIINQUIRY _IOR('H', 240, int)
/* HCI timeouts */ /* HCI timeouts */
#define HCI_CONN_TIMEOUT (HZ * 40) #define HCI_CONNECT_TIMEOUT (40000) /* 40 seconds */
#define HCI_DISCONN_TIMEOUT (HZ * 2) #define HCI_DISCONN_TIMEOUT (2000) /* 2 seconds */
#define HCI_CONN_IDLE_TIMEOUT (HZ * 60) #define HCI_IDLE_TIMEOUT (6000) /* 6 seconds */
#define HCI_INIT_TIMEOUT (10000) /* 10 seconds */
/* HCI Packet types */ /* HCI Packet types */
#define HCI_COMMAND_PKT 0x01 #define HCI_COMMAND_PKT 0x01
...@@ -145,7 +146,7 @@ enum { ...@@ -145,7 +146,7 @@ enum {
#define LMP_TACCURACY 0x10 #define LMP_TACCURACY 0x10
#define LMP_RSWITCH 0x20 #define LMP_RSWITCH 0x20
#define LMP_HOLD 0x40 #define LMP_HOLD 0x40
#define LMP_SNIF 0x80 #define LMP_SNIFF 0x80
#define LMP_PARK 0x01 #define LMP_PARK 0x01
#define LMP_RSSI 0x02 #define LMP_RSSI 0x02
...@@ -160,13 +161,21 @@ enum { ...@@ -160,13 +161,21 @@ enum {
#define LMP_PSCHEME 0x02 #define LMP_PSCHEME 0x02
#define LMP_PCONTROL 0x04 #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 */ /* Link policies */
#define HCI_LP_RSWITCH 0x0001 #define HCI_LP_RSWITCH 0x0001
#define HCI_LP_HOLD 0x0002 #define HCI_LP_HOLD 0x0002
#define HCI_LP_SNIFF 0x0004 #define HCI_LP_SNIFF 0x0004
#define HCI_LP_PARK 0x0008 #define HCI_LP_PARK 0x0008
/* Link mode */ /* Link modes */
#define HCI_LM_ACCEPT 0x8000 #define HCI_LM_ACCEPT 0x8000
#define HCI_LM_MASTER 0x0001 #define HCI_LM_MASTER 0x0001
#define HCI_LM_AUTH 0x0002 #define HCI_LM_AUTH 0x0002
...@@ -192,7 +201,7 @@ struct hci_rp_read_loc_version { ...@@ -192,7 +201,7 @@ struct hci_rp_read_loc_version {
} __attribute__ ((packed)); } __attribute__ ((packed));
#define OCF_READ_LOCAL_FEATURES 0x0003 #define OCF_READ_LOCAL_FEATURES 0x0003
struct hci_rp_read_loc_features { struct hci_rp_read_local_features {
__u8 status; __u8 status;
__u8 features[8]; __u8 features[8];
} __attribute__ ((packed)); } __attribute__ ((packed));
...@@ -376,17 +385,32 @@ struct hci_cp_change_conn_link_key { ...@@ -376,17 +385,32 @@ struct hci_cp_change_conn_link_key {
} __attribute__ ((packed)); } __attribute__ ((packed));
#define OCF_READ_REMOTE_FEATURES 0x001B #define OCF_READ_REMOTE_FEATURES 0x001B
struct hci_cp_read_rmt_features { struct hci_cp_read_remote_features {
__le16 handle; __le16 handle;
} __attribute__ ((packed)); } __attribute__ ((packed));
#define OCF_READ_REMOTE_VERSION 0x001D #define OCF_READ_REMOTE_VERSION 0x001D
struct hci_cp_read_rmt_version { struct hci_cp_read_remote_version {
__le16 handle; __le16 handle;
} __attribute__ ((packed)); } __attribute__ ((packed));
/* Link Policy */ /* 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 #define OCF_ROLE_DISCOVERY 0x0009
struct hci_cp_role_discovery { struct hci_cp_role_discovery {
__le16 handle; __le16 handle;
...@@ -423,6 +447,14 @@ struct hci_rp_write_link_policy { ...@@ -423,6 +447,14 @@ struct hci_rp_write_link_policy {
__le16 handle; __le16 handle;
} __attribute__ ((packed)); } __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 */ /* Status params */
#define OGF_STATUS_PARAM 0x05 #define OGF_STATUS_PARAM 0x05
...@@ -582,15 +614,15 @@ struct hci_ev_link_key_notify { ...@@ -582,15 +614,15 @@ struct hci_ev_link_key_notify {
__u8 key_type; __u8 key_type;
} __attribute__ ((packed)); } __attribute__ ((packed));
#define HCI_EV_RMT_FEATURES 0x0B #define HCI_EV_REMOTE_FEATURES 0x0B
struct hci_ev_rmt_features { struct hci_ev_remote_features {
__u8 status; __u8 status;
__le16 handle; __le16 handle;
__u8 features[8]; __u8 features[8];
} __attribute__ ((packed)); } __attribute__ ((packed));
#define HCI_EV_RMT_VERSION 0x0C #define HCI_EV_REMOTE_VERSION 0x0C
struct hci_ev_rmt_version { struct hci_ev_remote_version {
__u8 status; __u8 status;
__le16 handle; __le16 handle;
__u8 lmp_ver; __u8 lmp_ver;
...@@ -611,6 +643,16 @@ struct hci_ev_pscan_rep_mode { ...@@ -611,6 +643,16 @@ struct hci_ev_pscan_rep_mode {
__u8 pscan_rep_mode; __u8 pscan_rep_mode;
} __attribute__ ((packed)); } __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 */ /* Internal events generated by Bluetooth stack */
#define HCI_EV_STACK_INTERNAL 0xFD #define HCI_EV_STACK_INTERNAL 0xFD
struct hci_ev_stack_internal { struct hci_ev_stack_internal {
......
...@@ -31,10 +31,7 @@ ...@@ -31,10 +31,7 @@
#define HCI_PROTO_L2CAP 0 #define HCI_PROTO_L2CAP 0
#define HCI_PROTO_SCO 1 #define HCI_PROTO_SCO 1
#define HCI_INIT_TIMEOUT (HZ * 10)
/* HCI Core structures */ /* HCI Core structures */
struct inquiry_data { struct inquiry_data {
bdaddr_t bdaddr; bdaddr_t bdaddr;
__u8 pscan_rep_mode; __u8 pscan_rep_mode;
...@@ -81,6 +78,10 @@ struct hci_dev { ...@@ -81,6 +78,10 @@ struct hci_dev {
__u16 link_policy; __u16 link_policy;
__u16 link_mode; __u16 link_mode;
__u32 idle_timeout;
__u16 sniff_min_interval;
__u16 sniff_max_interval;
unsigned long quirks; unsigned long quirks;
atomic_t cmd_cnt; atomic_t cmd_cnt;
...@@ -145,17 +146,23 @@ struct hci_conn { ...@@ -145,17 +146,23 @@ struct hci_conn {
bdaddr_t dst; bdaddr_t dst;
__u16 handle; __u16 handle;
__u16 state; __u16 state;
__u8 mode;
__u8 type; __u8 type;
__u8 out; __u8 out;
__u8 dev_class[3]; __u8 dev_class[3];
__u8 features[8];
__u16 interval;
__u16 link_policy;
__u32 link_mode; __u32 link_mode;
__u8 power_save;
unsigned long pend; unsigned long pend;
unsigned int sent; unsigned int sent;
struct sk_buff_head data_q; struct sk_buff_head data_q;
struct timer_list timer; struct timer_list disc_timer;
struct timer_list idle_timer;
struct hci_dev *hdev; struct hci_dev *hdev;
void *l2cap_data; void *l2cap_data;
...@@ -211,7 +218,8 @@ void hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data); ...@@ -211,7 +218,8 @@ void hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data);
enum { enum {
HCI_CONN_AUTH_PEND, HCI_CONN_AUTH_PEND,
HCI_CONN_ENCRYPT_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) static inline void hci_conn_hash_init(struct hci_dev *hdev)
...@@ -286,31 +294,27 @@ int hci_conn_encrypt(struct hci_conn *conn); ...@@ -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_change_link_key(struct hci_conn *conn);
int hci_conn_switch_role(struct hci_conn *conn, uint8_t role); 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) void hci_conn_enter_active_mode(struct hci_conn *conn);
{ void hci_conn_enter_sniff_mode(struct hci_conn *conn);
mod_timer(&conn->timer, jiffies + timeout);
}
static inline void hci_conn_del_timer(struct hci_conn *conn)
{
del_timer(&conn->timer);
}
static inline void hci_conn_hold(struct hci_conn *conn) static inline void hci_conn_hold(struct hci_conn *conn)
{ {
atomic_inc(&conn->refcnt); atomic_inc(&conn->refcnt);
hci_conn_del_timer(conn); del_timer(&conn->disc_timer);
} }
static inline void hci_conn_put(struct hci_conn *conn) static inline void hci_conn_put(struct hci_conn *conn)
{ {
if (atomic_dec_and_test(&conn->refcnt)) { if (atomic_dec_and_test(&conn->refcnt)) {
unsigned long timeo;
if (conn->type == ACL_LINK) { if (conn->type == ACL_LINK) {
unsigned long timeo = (conn->out) ? timeo = msecs_to_jiffies(HCI_DISCONN_TIMEOUT);
HCI_DISCONN_TIMEOUT : HCI_DISCONN_TIMEOUT * 2; if (!conn->out)
hci_conn_set_timer(conn, timeo); timeo *= 2;
del_timer(&conn->idle_timer);
} else } 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); ...@@ -411,8 +415,10 @@ void hci_unregister_sysfs(struct hci_dev *hdev);
#define SET_HCIDEV_DEV(hdev, pdev) ((hdev)->class_dev.dev = (pdev)) #define SET_HCIDEV_DEV(hdev, pdev) ((hdev)->class_dev.dev = (pdev))
/* ----- LMP capabilities ----- */ /* ----- LMP capabilities ----- */
#define lmp_rswitch_capable(dev) (dev->features[0] & LMP_RSWITCH) #define lmp_rswitch_capable(dev) ((dev)->features[0] & LMP_RSWITCH)
#define lmp_encrypt_capable(dev) (dev->features[0] & LMP_ENCRYPT) #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 ----- */ /* ----- HCI protocols ----- */
struct hci_proto { struct hci_proto {
......
...@@ -48,7 +48,7 @@ ...@@ -48,7 +48,7 @@
#define BT_DBG(D...) #define BT_DBG(D...)
#endif #endif
#define VERSION "2.8" #define VERSION "2.9"
/* Bluetooth sockets */ /* Bluetooth sockets */
#define BT_MAX_PROTO 8 #define BT_MAX_PROTO 8
......
...@@ -115,7 +115,7 @@ void hci_add_sco(struct hci_conn *conn, __u16 handle) ...@@ -115,7 +115,7 @@ void hci_add_sco(struct hci_conn *conn, __u16 handle)
static void hci_conn_timeout(unsigned long arg) static void hci_conn_timeout(unsigned long arg)
{ {
struct hci_conn *conn = (void *)arg; struct hci_conn *conn = (void *) arg;
struct hci_dev *hdev = conn->hdev; struct hci_dev *hdev = conn->hdev;
BT_DBG("conn %p state %d", conn, conn->state); BT_DBG("conn %p state %d", conn, conn->state);
...@@ -132,11 +132,13 @@ static void hci_conn_timeout(unsigned long arg) ...@@ -132,11 +132,13 @@ static void hci_conn_timeout(unsigned long arg)
return; return;
} }
static void hci_conn_init_timer(struct hci_conn *conn) static void hci_conn_idle(unsigned long arg)
{ {
init_timer(&conn->timer); struct hci_conn *conn = (void *) arg;
conn->timer.function = hci_conn_timeout;
conn->timer.data = (unsigned long)conn; 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) 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) ...@@ -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)); 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; return NULL;
memset(conn, 0, sizeof(struct hci_conn));
bacpy(&conn->dst, dst); bacpy(&conn->dst, dst);
conn->type = type;
conn->hdev = hdev; conn->hdev = hdev;
conn->type = type;
conn->mode = HCI_CM_ACTIVE;
conn->state = BT_OPEN; conn->state = BT_OPEN;
conn->power_save = 1;
skb_queue_head_init(&conn->data_q); 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); atomic_set(&conn->refcnt, 0);
...@@ -178,7 +190,9 @@ int hci_conn_del(struct hci_conn *conn) ...@@ -178,7 +190,9 @@ int hci_conn_del(struct hci_conn *conn)
BT_DBG("%s conn %p handle %d", hdev->name, conn, conn->handle); 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) { if (conn->type == SCO_LINK) {
struct hci_conn *acl = conn->link; struct hci_conn *acl = conn->link;
...@@ -364,6 +378,70 @@ int hci_conn_switch_role(struct hci_conn *conn, uint8_t role) ...@@ -364,6 +378,70 @@ int hci_conn_switch_role(struct hci_conn *conn, uint8_t role)
} }
EXPORT_SYMBOL(hci_conn_switch_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 */ /* Drop all connection on the device */
void hci_conn_hash_flush(struct hci_dev *hdev) void hci_conn_hash_flush(struct hci_dev *hdev)
{ {
......
...@@ -411,7 +411,7 @@ int hci_inquiry(void __user *arg) ...@@ -411,7 +411,7 @@ int hci_inquiry(void __user *arg)
} }
hci_dev_unlock_bh(hdev); 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) if (do_inquiry && (err = hci_request(hdev, hci_inq_req, (unsigned long)&ir, timeo)) < 0)
goto done; goto done;
...@@ -479,7 +479,8 @@ int hci_dev_open(__u16 dev) ...@@ -479,7 +479,8 @@ int hci_dev_open(__u16 dev)
set_bit(HCI_INIT, &hdev->flags); set_bit(HCI_INIT, &hdev->flags);
//__hci_request(hdev, hci_reset_req, 0, HZ); //__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); clear_bit(HCI_INIT, &hdev->flags);
} }
...@@ -546,7 +547,8 @@ static int hci_dev_do_close(struct hci_dev *hdev) ...@@ -546,7 +547,8 @@ static int hci_dev_do_close(struct hci_dev *hdev)
atomic_set(&hdev->cmd_cnt, 1); atomic_set(&hdev->cmd_cnt, 1);
if (!test_bit(HCI_RAW, &hdev->flags)) { if (!test_bit(HCI_RAW, &hdev->flags)) {
set_bit(HCI_INIT, &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); clear_bit(HCI_INIT, &hdev->flags);
} }
...@@ -619,7 +621,8 @@ int hci_dev_reset(__u16 dev) ...@@ -619,7 +621,8 @@ int hci_dev_reset(__u16 dev)
hdev->acl_cnt = 0; hdev->sco_cnt = 0; hdev->acl_cnt = 0; hdev->sco_cnt = 0;
if (!test_bit(HCI_RAW, &hdev->flags)) 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: done:
tasklet_enable(&hdev->tx_task); tasklet_enable(&hdev->tx_task);
...@@ -657,7 +660,8 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg) ...@@ -657,7 +660,8 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg)
switch (cmd) { switch (cmd) {
case HCISETAUTH: 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; break;
case HCISETENCRYPT: case HCISETENCRYPT:
...@@ -668,18 +672,19 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg) ...@@ -668,18 +672,19 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg)
if (!test_bit(HCI_AUTH, &hdev->flags)) { if (!test_bit(HCI_AUTH, &hdev->flags)) {
/* Auth must be enabled first */ /* Auth must be enabled first */
err = hci_request(hdev, hci_auth_req, err = hci_request(hdev, hci_auth_req, dr.dev_opt,
dr.dev_opt, HCI_INIT_TIMEOUT); msecs_to_jiffies(HCI_INIT_TIMEOUT));
if (err) if (err)
break; break;
} }
err = hci_request(hdev, hci_encrypt_req, err = hci_request(hdev, hci_encrypt_req, dr.dev_opt,
dr.dev_opt, HCI_INIT_TIMEOUT); msecs_to_jiffies(HCI_INIT_TIMEOUT));
break; break;
case HCISETSCAN: 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; break;
case HCISETPTYPE: case HCISETPTYPE:
...@@ -848,6 +853,10 @@ int hci_register_dev(struct hci_dev *hdev) ...@@ -848,6 +853,10 @@ int hci_register_dev(struct hci_dev *hdev)
hdev->pkt_type = (HCI_DM1 | HCI_DH1 | HCI_HV1); hdev->pkt_type = (HCI_DM1 | HCI_DH1 | HCI_HV1);
hdev->link_mode = (HCI_LM_ACCEPT); 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->cmd_task, hci_cmd_task,(unsigned long) hdev);
tasklet_init(&hdev->rx_task, hci_rx_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); 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) ...@@ -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 (hdev->acl_cnt && (conn = hci_low_sent(hdev, ACL_LINK, &quote))) {
while (quote-- && (skb = skb_dequeue(&conn->data_q))) { while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
BT_DBG("skb %p len %d", skb, skb->len); BT_DBG("skb %p len %d", skb, skb->len);
hci_conn_enter_active_mode(conn);
hci_send_frame(skb); hci_send_frame(skb);
hdev->acl_last_tx = jiffies; hdev->acl_last_tx = jiffies;
...@@ -1298,6 +1310,8 @@ static inline void hci_acldata_packet(struct hci_dev *hdev, struct sk_buff *skb) ...@@ -1298,6 +1310,8 @@ static inline void hci_acldata_packet(struct hci_dev *hdev, struct sk_buff *skb)
if (conn) { if (conn) {
register struct hci_proto *hp; register struct hci_proto *hp;
hci_conn_enter_active_mode(conn);
/* Send to upper protocol */ /* Send to upper protocol */
if ((hp = hci_proto[HCI_PROTO_L2CAP]) && hp->recv_acldata) { if ((hp = hci_proto[HCI_PROTO_L2CAP]) && hp->recv_acldata) {
hp->recv_acldata(conn, skb, flags); hp->recv_acldata(conn, skb, flags);
......
This diff is collapsed.
...@@ -61,18 +61,106 @@ static ssize_t show_inquiry_cache(struct class_device *cdev, char *buf) ...@@ -61,18 +61,106 @@ static ssize_t show_inquiry_cache(struct class_device *cdev, char *buf)
return n; 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(name, S_IRUGO, show_name, NULL);
static CLASS_DEVICE_ATTR(type, S_IRUGO, show_type, 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(address, S_IRUGO, show_address, NULL);
static CLASS_DEVICE_ATTR(flags, S_IRUGO, show_flags, 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(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[] = { static struct class_device_attribute *bt_attrs[] = {
&class_device_attr_name, &class_device_attr_name,
&class_device_attr_type, &class_device_attr_type,
&class_device_attr_address, &class_device_attr_address,
&class_device_attr_flags, &class_device_attr_flags,
&class_device_attr_inquiry_cache, &class_device_attr_inquiry_cache,
&class_device_attr_idle_timeout,
&class_device_attr_sniff_max_interval,
&class_device_attr_sniff_min_interval,
NULL NULL
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment