mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-29 07:04:10 +08:00
[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: Ulisses Furquim <ulissesf@gmail.com> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
This commit is contained in:
parent
da1f519851
commit
04837f6447
@ -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 {
|
||||
|
@ -31,10 +31,7 @@
|
||||
#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 {
|
||||
|
@ -48,7 +48,7 @@
|
||||
#define BT_DBG(D...)
|
||||
#endif
|
||||
|
||||
#define VERSION "2.8"
|
||||
#define VERSION "2.9"
|
||||
|
||||
/* Bluetooth sockets */
|
||||
#define BT_MAX_PROTO 8
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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, "e))) {
|
||||
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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/* Authentication Complete */
|
||||
static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
/* Mode Change */
|
||||
static inline void hci_mode_change_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_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, handle);
|
||||
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;
|
||||
|
||||
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) {
|
||||
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
|
||||
{
|
||||
}
|
||||
|
||||
/* Clock Offset */
|
||||
static inline void hci_clock_offset_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
/* Remote Features */
|
||||
static inline void hci_remote_features_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_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, handle);
|
||||
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;
|
||||
|
||||
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) {
|
||||
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));
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user