X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=include%2Fnet%2Fbluetooth%2Fhci_core.h;h=c0fc39620f3643c4393ba2b1d99c8181498b0da0;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=8308881479a82d71d64973b9f3d9b25c2b39fb7f;hpb=9213980e6a70d8473e0ffd4b39ab5b6caaba9ff5;p=linux-2.6.git diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 830888147..c0fc39620 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -25,23 +25,27 @@ #ifndef __HCI_CORE_H #define __HCI_CORE_H -#include #include /* HCI upper protocols */ #define HCI_PROTO_L2CAP 0 #define HCI_PROTO_SCO 1 -#define HCI_INIT_TIMEOUT (HZ * 10) - -extern struct proc_dir_entry *proc_bt_hci; - /* HCI Core structures */ +struct inquiry_data { + bdaddr_t bdaddr; + __u8 pscan_rep_mode; + __u8 pscan_period_mode; + __u8 pscan_mode; + __u8 dev_class[3]; + __le16 clock_offset; + __s8 rssi; +}; struct inquiry_entry { struct inquiry_entry *next; __u32 timestamp; - struct inquiry_info info; + struct inquiry_data data; }; struct inquiry_cache { @@ -53,7 +57,8 @@ struct inquiry_cache { struct hci_conn_hash { struct list_head list; spinlock_t lock; - unsigned int num; + unsigned int acl_num; + unsigned int sco_num; }; struct hci_dev { @@ -67,12 +72,19 @@ struct hci_dev { __u8 type; bdaddr_t bdaddr; __u8 features[8]; + __u8 hci_ver; + __u16 hci_rev; + __u16 manufacturer; __u16 voice_setting; __u16 pkt_type; __u16 link_policy; __u16 link_mode; + __u32 idle_timeout; + __u16 sniff_min_interval; + __u16 sniff_max_interval; + unsigned long quirks; atomic_t cmd_cnt; @@ -108,16 +120,15 @@ struct hci_dev { struct hci_dev_stats stat; + struct sk_buff_head driver_init; + void *driver_data; void *core_data; atomic_t promisc; -#ifdef CONFIG_PROC_FS - struct proc_dir_entry *proc; -#endif - - struct class_device class_dev; + struct device *parent; + struct device dev; struct module *owner; @@ -139,17 +150,29 @@ struct hci_conn { bdaddr_t dst; __u16 handle; __u16 state; + __u8 mode; __u8 type; __u8 out; + __u8 attempt; + __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 work_struct work; + + struct device dev; + struct hci_dev *hdev; void *l2cap_data; void *sco_data; @@ -160,7 +183,9 @@ struct hci_conn { extern struct hci_proto *hci_proto[]; extern struct list_head hci_dev_list; +extern struct list_head hci_cb_list; extern rwlock_t hci_dev_list_lock; +extern rwlock_t hci_cb_list_lock; /* ----- Inquiry cache ----- */ #define INQUIRY_CACHE_AGE_MAX (HZ*30) // 30 seconds @@ -195,15 +220,15 @@ static inline long inquiry_entry_age(struct inquiry_entry *e) return jiffies - e->timestamp; } -struct inquiry_entry *inquiry_cache_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr); -void inquiry_cache_update(struct hci_dev *hdev, struct inquiry_info *info); -void inquiry_cache_flush(struct hci_dev *hdev); -int inquiry_cache_dump(struct hci_dev *hdev, int num, __u8 *buf); +struct inquiry_entry *hci_inquiry_cache_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr); +void hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data); /* ----- HCI Connections ----- */ enum { HCI_CONN_AUTH_PEND, - HCI_CONN_ENCRYPT_PEND + HCI_CONN_ENCRYPT_PEND, + HCI_CONN_RSWITCH_PEND, + HCI_CONN_MODE_CHANGE_PEND, }; static inline void hci_conn_hash_init(struct hci_dev *hdev) @@ -211,21 +236,28 @@ static inline void hci_conn_hash_init(struct hci_dev *hdev) struct hci_conn_hash *h = &hdev->conn_hash; INIT_LIST_HEAD(&h->list); spin_lock_init(&h->lock); - h->num = 0; + h->acl_num = 0; + h->sco_num = 0; } static inline void hci_conn_hash_add(struct hci_dev *hdev, struct hci_conn *c) { struct hci_conn_hash *h = &hdev->conn_hash; list_add(&c->list, &h->list); - h->num++; + if (c->type == ACL_LINK) + h->acl_num++; + else + h->sco_num++; } static inline void hci_conn_hash_del(struct hci_dev *hdev, struct hci_conn *c) { struct hci_conn_hash *h = &hdev->conn_hash; list_del(&c->list); - h->num--; + if (c->type == ACL_LINK) + h->acl_num--; + else + h->sco_num--; } static inline struct hci_conn *hci_conn_hash_lookup_handle(struct hci_dev *hdev, @@ -258,6 +290,21 @@ static inline struct hci_conn *hci_conn_hash_lookup_ba(struct hci_dev *hdev, return NULL; } +static inline struct hci_conn *hci_conn_hash_lookup_state(struct hci_dev *hdev, + __u8 type, __u16 state) +{ + struct hci_conn_hash *h = &hdev->conn_hash; + struct list_head *p; + struct hci_conn *c; + + list_for_each(p, &h->list) { + c = list_entry(p, struct hci_conn, list); + if (c->type == type && c->state == state) + return c; + } + return NULL; +} + void hci_acl_connect(struct hci_conn *conn); void hci_acl_disconn(struct hci_conn *conn, __u8 reason); void hci_add_sco(struct hci_conn *conn, __u16 handle); @@ -269,32 +316,33 @@ void hci_conn_hash_flush(struct hci_dev *hdev); struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *src); int hci_conn_auth(struct hci_conn *conn); 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); + del_timer(&conn->idle_timer); + if (conn->state == BT_CONNECTED) { + timeo = msecs_to_jiffies(HCI_DISCONN_TIMEOUT); + if (!conn->out) + timeo *= 2; + } else + timeo = msecs_to_jiffies(10); } else - hci_conn_set_timer(conn, HZ / 100); + timeo = msecs_to_jiffies(10); + mod_timer(&conn->disc_timer, jiffies + timeo); } } @@ -381,7 +429,7 @@ static inline int hci_recv_frame(struct sk_buff *skb) bt_cb(skb)->incoming = 1; /* Time stamp */ - do_gettimeofday(&skb->stamp); + __net_timestamp(skb); /* Queue frame for rx task */ skb_queue_tail(&hdev->rx_q, skb); @@ -391,12 +439,16 @@ static inline int hci_recv_frame(struct sk_buff *skb) int hci_register_sysfs(struct hci_dev *hdev); void hci_unregister_sysfs(struct hci_dev *hdev); +void hci_conn_add_sysfs(struct hci_conn *conn); +void hci_conn_del_sysfs(struct hci_conn *conn); -#define SET_HCIDEV_DEV(hdev, pdev) ((hdev)->class_dev.dev = (pdev)) +#define SET_HCIDEV_DEV(hdev, pdev) ((hdev)->parent = (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 { @@ -485,6 +537,78 @@ static inline void hci_proto_encrypt_cfm(struct hci_conn *conn, __u8 status) int hci_register_proto(struct hci_proto *hproto); int hci_unregister_proto(struct hci_proto *hproto); + +/* ----- HCI callbacks ----- */ +struct hci_cb { + struct list_head list; + + char *name; + + void (*auth_cfm) (struct hci_conn *conn, __u8 status); + void (*encrypt_cfm) (struct hci_conn *conn, __u8 status, __u8 encrypt); + void (*key_change_cfm) (struct hci_conn *conn, __u8 status); + void (*role_switch_cfm) (struct hci_conn *conn, __u8 status, __u8 role); +}; + +static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status) +{ + struct list_head *p; + + hci_proto_auth_cfm(conn, status); + + read_lock_bh(&hci_cb_list_lock); + list_for_each(p, &hci_cb_list) { + struct hci_cb *cb = list_entry(p, struct hci_cb, list); + if (cb->auth_cfm) + cb->auth_cfm(conn, status); + } + read_unlock_bh(&hci_cb_list_lock); +} + +static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status, __u8 encrypt) +{ + struct list_head *p; + + hci_proto_encrypt_cfm(conn, status); + + read_lock_bh(&hci_cb_list_lock); + list_for_each(p, &hci_cb_list) { + struct hci_cb *cb = list_entry(p, struct hci_cb, list); + if (cb->encrypt_cfm) + cb->encrypt_cfm(conn, status, encrypt); + } + read_unlock_bh(&hci_cb_list_lock); +} + +static inline void hci_key_change_cfm(struct hci_conn *conn, __u8 status) +{ + struct list_head *p; + + read_lock_bh(&hci_cb_list_lock); + list_for_each(p, &hci_cb_list) { + struct hci_cb *cb = list_entry(p, struct hci_cb, list); + if (cb->key_change_cfm) + cb->key_change_cfm(conn, status); + } + read_unlock_bh(&hci_cb_list_lock); +} + +static inline void hci_role_switch_cfm(struct hci_conn *conn, __u8 status, __u8 role) +{ + struct list_head *p; + + read_lock_bh(&hci_cb_list_lock); + list_for_each(p, &hci_cb_list) { + struct hci_cb *cb = list_entry(p, struct hci_cb, list); + if (cb->role_switch_cfm) + cb->role_switch_cfm(conn, status, role); + } + read_unlock_bh(&hci_cb_list_lock); +} + +int hci_register_cb(struct hci_cb *hcb); +int hci_unregister_cb(struct hci_cb *hcb); + int hci_register_notifier(struct notifier_block *nb); int hci_unregister_notifier(struct notifier_block *nb); @@ -500,8 +624,10 @@ void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data); void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb); /* HCI info for socket */ -#define hci_pi(sk) ((struct hci_pinfo *)sk->sk_protinfo) +#define hci_pi(sk) ((struct hci_pinfo *) sk) + struct hci_pinfo { + struct bt_sock bt; struct hci_dev *hdev; struct hci_filter filter; __u32 cmsg_mask; @@ -525,6 +651,5 @@ struct hci_sec_filter { #define hci_req_unlock(d) up(&d->req_lock) void hci_req_complete(struct hci_dev *hdev, int result); -void hci_req_cancel(struct hci_dev *hdev, int err); #endif /* __HCI_CORE_H */