]> rtime.felk.cvut.cz Git - lisovros/linux_canprio.git/commitdiff
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/padovan/blueto...
authorJohn W. Linville <linville@tuxdriver.com>
Wed, 9 Nov 2011 19:54:33 +0000 (14:54 -0500)
committerJohn W. Linville <linville@tuxdriver.com>
Wed, 9 Nov 2011 19:54:33 +0000 (14:54 -0500)
Conflicts:
net/bluetooth/l2cap_sock.c
net/bluetooth/mgmt.c

27 files changed:
drivers/bluetooth/ath3k.c
drivers/bluetooth/bfusb.c
drivers/bluetooth/bluecard_cs.c
drivers/bluetooth/bt3c_cs.c
drivers/bluetooth/btuart_cs.c
drivers/bluetooth/btusb.c
drivers/bluetooth/dtl1_cs.c
drivers/bluetooth/hci_vhci.c
include/net/bluetooth/bluetooth.h
include/net/bluetooth/hci.h
include/net/bluetooth/hci_core.h
include/net/bluetooth/l2cap.h
include/net/bluetooth/mgmt.h
net/bluetooth/bnep/core.c
net/bluetooth/cmtp/core.c
net/bluetooth/hci_conn.c
net/bluetooth/hci_core.c
net/bluetooth/hci_event.c
net/bluetooth/hci_sysfs.c
net/bluetooth/hidp/core.c
net/bluetooth/l2cap_core.c
net/bluetooth/l2cap_sock.c
net/bluetooth/mgmt.c
net/bluetooth/rfcomm/core.c
net/bluetooth/rfcomm/sock.c
net/bluetooth/rfcomm/tty.c
net/bluetooth/smp.c

index 106beb194f3c8d438e105dab8d12aaecb8716103..1622772f802d9b4c2a94ad48ae62f16e3518a44b 100644 (file)
@@ -30,6 +30,7 @@
 #include <net/bluetooth/bluetooth.h>
 
 #define VERSION "1.0"
+#define ATH3K_FIRMWARE "ath3k-1.fw"
 
 #define ATH3K_DNLOAD                           0x01
 #define ATH3K_GETSTATE                         0x05
@@ -400,9 +401,15 @@ static int ath3k_probe(struct usb_interface *intf,
                return 0;
        }
 
-       if (request_firmware(&firmware, "ath3k-1.fw", &udev->dev) < 0) {
-               BT_ERR("Error loading firmware");
-               return -EIO;
+       ret = request_firmware(&firmware, ATH3K_FIRMWARE, &udev->dev);
+       if (ret < 0) {
+               if (ret == -ENOENT)
+                       BT_ERR("Firmware file \"%s\" not found",
+                                                       ATH3K_FIRMWARE);
+               else
+                       BT_ERR("Firmware file \"%s\" request failed (err=%d)",
+                                                       ATH3K_FIRMWARE, ret);
+               return ret;
        }
 
        ret = ath3k_load_firmware(udev, firmware);
@@ -441,4 +448,4 @@ MODULE_AUTHOR("Atheros Communications");
 MODULE_DESCRIPTION("Atheros AR30xx firmware driver");
 MODULE_VERSION(VERSION);
 MODULE_LICENSE("GPL");
-MODULE_FIRMWARE("ath3k-1.fw");
+MODULE_FIRMWARE(ATH3K_FIRMWARE);
index 61b591470a90827280005b50b000907a7e7c2ac2..a936763b8c3d97885baa577daa547b822c30ee79 100644 (file)
@@ -751,9 +751,7 @@ static void bfusb_disconnect(struct usb_interface *intf)
 
        bfusb_close(hdev);
 
-       if (hci_unregister_dev(hdev) < 0)
-               BT_ERR("Can't unregister HCI device %s", hdev->name);
-
+       hci_unregister_dev(hdev);
        hci_free_dev(hdev);
 }
 
index aed1904ea67b54f54d1c033073a6af281f3b0070..c6a0c6103743838fef1dd30157681e9d916115c7 100644 (file)
@@ -844,9 +844,7 @@ static int bluecard_close(bluecard_info_t *info)
        /* Turn FPGA off */
        outb(0x80, iobase + 0x30);
 
-       if (hci_unregister_dev(hdev) < 0)
-               BT_ERR("Can't unregister HCI device %s", hdev->name);
-
+       hci_unregister_dev(hdev);
        hci_free_dev(hdev);
 
        return 0;
index 4fc01949d39981a77b6e9dca90606ab94ae259b8..0c97e5d514b635b0574d3832d6b593263146f56e 100644 (file)
@@ -636,9 +636,7 @@ static int bt3c_close(bt3c_info_t *info)
 
        bt3c_hci_close(hdev);
 
-       if (hci_unregister_dev(hdev) < 0)
-               BT_ERR("Can't unregister HCI device %s", hdev->name);
-
+       hci_unregister_dev(hdev);
        hci_free_dev(hdev);
 
        return 0;
index 526b61807d945dda8a6983758b348fe7ea0bd233..200b3a2877d69c43754d6210bc0ad2feb5be81c4 100644 (file)
@@ -565,9 +565,7 @@ static int btuart_close(btuart_info_t *info)
 
        spin_unlock_irqrestore(&(info->lock), flags);
 
-       if (hci_unregister_dev(hdev) < 0)
-               BT_ERR("Can't unregister HCI device %s", hdev->name);
-
+       hci_unregister_dev(hdev);
        hci_free_dev(hdev);
 
        return 0;
index fe4ebc375b3dafd274da41c803cbb9ac37754146..2bd87d45f1c2313be3efc5a3728a8fcbe687b0d7 100644 (file)
@@ -315,7 +315,8 @@ static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t mem_flags)
 
        err = usb_submit_urb(urb, mem_flags);
        if (err < 0) {
-               BT_ERR("%s urb %p submission failed (%d)",
+               if (err != -EPERM && err != -ENODEV)
+                       BT_ERR("%s urb %p submission failed (%d)",
                                                hdev->name, urb, -err);
                usb_unanchor_urb(urb);
        }
@@ -400,7 +401,8 @@ static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t mem_flags)
 
        err = usb_submit_urb(urb, mem_flags);
        if (err < 0) {
-               BT_ERR("%s urb %p submission failed (%d)",
+               if (err != -EPERM && err != -ENODEV)
+                       BT_ERR("%s urb %p submission failed (%d)",
                                                hdev->name, urb, -err);
                usb_unanchor_urb(urb);
        }
@@ -523,7 +525,8 @@ static int btusb_submit_isoc_urb(struct hci_dev *hdev, gfp_t mem_flags)
 
        err = usb_submit_urb(urb, mem_flags);
        if (err < 0) {
-               BT_ERR("%s urb %p submission failed (%d)",
+               if (err != -EPERM && err != -ENODEV)
+                       BT_ERR("%s urb %p submission failed (%d)",
                                                hdev->name, urb, -err);
                usb_unanchor_urb(urb);
        }
@@ -727,6 +730,9 @@ static int btusb_send_frame(struct sk_buff *skb)
                usb_fill_bulk_urb(urb, data->udev, pipe,
                                skb->data, skb->len, btusb_tx_complete, skb);
 
+               if (skb->priority >= HCI_PRIO_MAX - 1)
+                       urb->transfer_flags  = URB_ISO_ASAP;
+
                hdev->stat.acl_tx++;
                break;
 
@@ -770,7 +776,9 @@ skip_waking:
 
        err = usb_submit_urb(urb, GFP_ATOMIC);
        if (err < 0) {
-               BT_ERR("%s urb %p submission failed", hdev->name, urb);
+               if (err != -EPERM && err != -ENODEV)
+                       BT_ERR("%s urb %p submission failed (%d)",
+                                               hdev->name, urb, -err);
                kfree(urb->setup_packet);
                usb_unanchor_urb(urb);
        } else {
index 5e4c2de9fc3f778bf71c71c89d9222edce4bcd36..969bb22e493f530977ab1c5258f55067963a6cac 100644 (file)
@@ -551,9 +551,7 @@ static int dtl1_close(dtl1_info_t *info)
 
        spin_unlock_irqrestore(&(info->lock), flags);
 
-       if (hci_unregister_dev(hdev) < 0)
-               BT_ERR("Can't unregister HCI device %s", hdev->name);
-
+       hci_unregister_dev(hdev);
        hci_free_dev(hdev);
 
        return 0;
index 67c180c2c1e0fbf27951862e30a0787deba4d4a0..2e302a11ab5528ccd078f41f9439c1067fd9704f 100644 (file)
@@ -264,10 +264,7 @@ static int vhci_release(struct inode *inode, struct file *file)
        struct vhci_data *data = file->private_data;
        struct hci_dev *hdev = data->hdev;
 
-       if (hci_unregister_dev(hdev) < 0) {
-               BT_ERR("Can't unregister HCI device %s", hdev->name);
-       }
-
+       hci_unregister_dev(hdev);
        hci_free_dev(hdev);
 
        file->private_data = NULL;
index e727555d4ee9bc479df54d32fc5b9e6ce9ac52cd..38cd3dab7f1d297359c1b94356f93e13d2012fb9 100644 (file)
@@ -77,6 +77,33 @@ struct bt_power {
 #define BT_POWER_FORCE_ACTIVE_OFF 0
 #define BT_POWER_FORCE_ACTIVE_ON  1
 
+#define BT_CHANNEL_POLICY      10
+
+/* BR/EDR only (default policy)
+ *   AMP controllers cannot be used.
+ *   Channel move requests from the remote device are denied.
+ *   If the L2CAP channel is currently using AMP, move the channel to BR/EDR.
+ */
+#define BT_CHANNEL_POLICY_BREDR_ONLY           0
+
+/* BR/EDR Preferred
+ *   Allow use of AMP controllers.
+ *   If the L2CAP channel is currently on AMP, move it to BR/EDR.
+ *   Channel move requests from the remote device are allowed.
+ */
+#define BT_CHANNEL_POLICY_BREDR_PREFERRED      1
+
+/* AMP Preferred
+ *   Allow use of AMP controllers
+ *   If the L2CAP channel is currently on BR/EDR and AMP controller
+ *     resources are available, initiate a channel move to AMP.
+ *   Channel move requests from the remote device are allowed.
+ *   If the L2CAP socket has not been connected yet, try to create
+ *     and configure the channel directly on an AMP controller rather
+ *     than BR/EDR.
+ */
+#define BT_CHANNEL_POLICY_AMP_PREFERRED                2
+
 __attribute__((format (printf, 2, 3)))
 int bt_printk(const char *level, const char *fmt, ...);
 
@@ -158,7 +185,7 @@ struct bt_skb_cb {
        __u8 pkt_type;
        __u8 incoming;
        __u16 expect;
-       __u8 tx_seq;
+       __u16 tx_seq;
        __u8 retries;
        __u8 sar;
        unsigned short channel;
index aaf79af7243262592607924aa1e4dd609da7d2aa..139ce2aa6eee700ba396343a268eda4962daa142 100644 (file)
@@ -264,6 +264,13 @@ enum {
 #define HCI_LK_SMP_IRK                 0x82
 #define HCI_LK_SMP_CSRK                        0x83
 
+/* ---- HCI Error Codes ---- */
+#define HCI_ERROR_AUTH_FAILURE         0x05
+#define HCI_ERROR_REJ_BAD_ADDR         0x0f
+#define HCI_ERROR_REMOTE_USER_TERM     0x13
+#define HCI_ERROR_LOCAL_HOST_TERM      0x16
+#define HCI_ERROR_PAIRING_NOT_ALLOWED  0x18
+
 /* -----  HCI Commands ---- */
 #define HCI_OP_NOP                     0x0000
 
@@ -726,6 +733,21 @@ struct hci_cp_write_page_scan_activity {
        #define PAGE_SCAN_TYPE_STANDARD         0x00
        #define PAGE_SCAN_TYPE_INTERLACED       0x01
 
+#define HCI_OP_READ_LOCAL_AMP_INFO     0x1409
+struct hci_rp_read_local_amp_info {
+       __u8     status;
+       __u8     amp_status;
+       __le32   total_bw;
+       __le32   max_bw;
+       __le32   min_latency;
+       __le32   max_pdu;
+       __u8     amp_type;
+       __le16   pal_cap;
+       __le16   max_assoc_size;
+       __le32   max_flush_to;
+       __le32   be_flush_to;
+} __packed;
+
 #define HCI_OP_LE_SET_EVENT_MASK       0x2001
 struct hci_cp_le_set_event_mask {
        __u8     mask[8];
index 5b924423cf205ad9e927c7de4045e86160f1a082..0a5a05d9109cf569131e7c6fa9bc5e2c5b08a640 100644 (file)
@@ -32,6 +32,9 @@
 #define HCI_PROTO_L2CAP        0
 #define HCI_PROTO_SCO  1
 
+/* HCI priority */
+#define HCI_PRIO_MAX   7
+
 /* HCI Core structures */
 struct inquiry_data {
        bdaddr_t        bdaddr;
@@ -64,6 +67,12 @@ struct hci_conn_hash {
        unsigned int     le_num;
 };
 
+struct hci_chan_hash {
+       struct list_head list;
+       spinlock_t       lock;
+       unsigned int     num;
+};
+
 struct bdaddr_list {
        struct list_head list;
        bdaddr_t bdaddr;
@@ -150,6 +159,17 @@ struct hci_dev {
        __u16           sniff_min_interval;
        __u16           sniff_max_interval;
 
+       __u8            amp_status;
+       __u32           amp_total_bw;
+       __u32           amp_max_bw;
+       __u32           amp_min_latency;
+       __u32           amp_max_pdu;
+       __u8            amp_type;
+       __u16           amp_pal_cap;
+       __u16           amp_assoc_size;
+       __u32           amp_max_flush_to;
+       __u32           amp_be_flush_to;
+
        unsigned int    auto_accept_delay;
 
        unsigned long   quirks;
@@ -173,8 +193,10 @@ struct hci_dev {
        struct workqueue_struct *workqueue;
 
        struct work_struct      power_on;
-       struct work_struct      power_off;
-       struct timer_list       off_timer;
+       struct delayed_work     power_off;
+
+       __u16                   discov_timeout;
+       struct delayed_work     discov_off;
 
        struct timer_list       cmd_timer;
        struct tasklet_struct   cmd_task;
@@ -195,6 +217,8 @@ struct hci_dev {
 
        __u16                   init_last_cmd;
 
+       struct list_head        mgmt_pending;
+
        struct inquiry_cache    inq_cache;
        struct hci_conn_hash    conn_hash;
        struct list_head        blacklist;
@@ -273,6 +297,7 @@ struct hci_conn {
        unsigned int    sent;
 
        struct sk_buff_head data_q;
+       struct hci_chan_hash chan_hash;
 
        struct timer_list disc_timer;
        struct timer_list idle_timer;
@@ -295,6 +320,14 @@ struct hci_conn {
        void (*disconn_cfm_cb)  (struct hci_conn *conn, u8 reason);
 };
 
+struct hci_chan {
+       struct list_head list;
+
+       struct hci_conn *conn;
+       struct sk_buff_head data_q;
+       unsigned int    sent;
+};
+
 extern struct hci_proto *hci_proto[];
 extern struct list_head hci_dev_list;
 extern struct list_head hci_cb_list;
@@ -455,6 +488,28 @@ static inline struct hci_conn *hci_conn_hash_lookup_state(struct hci_dev *hdev,
        return NULL;
 }
 
+static inline void hci_chan_hash_init(struct hci_conn *c)
+{
+       struct hci_chan_hash *h = &c->chan_hash;
+       INIT_LIST_HEAD(&h->list);
+       spin_lock_init(&h->lock);
+       h->num = 0;
+}
+
+static inline void hci_chan_hash_add(struct hci_conn *c, struct hci_chan *chan)
+{
+       struct hci_chan_hash *h = &c->chan_hash;
+       list_add(&chan->list, &h->list);
+       h->num++;
+}
+
+static inline void hci_chan_hash_del(struct hci_conn *c, struct hci_chan *chan)
+{
+       struct hci_chan_hash *h = &c->chan_hash;
+       list_del(&chan->list);
+       h->num--;
+}
+
 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);
@@ -466,6 +521,10 @@ int hci_conn_del(struct hci_conn *conn);
 void hci_conn_hash_flush(struct hci_dev *hdev);
 void hci_conn_check_pending(struct hci_dev *hdev);
 
+struct hci_chan *hci_chan_create(struct hci_conn *conn);
+int hci_chan_del(struct hci_chan *chan);
+void hci_chan_hash_flush(struct hci_conn *conn);
+
 struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst,
                                                __u8 sec_level, __u8 auth_type);
 int hci_conn_check_link_mode(struct hci_conn *conn);
@@ -543,7 +602,7 @@ struct hci_dev *hci_get_route(bdaddr_t *src, bdaddr_t *dst);
 struct hci_dev *hci_alloc_dev(void);
 void hci_free_dev(struct hci_dev *hdev);
 int hci_register_dev(struct hci_dev *hdev);
-int hci_unregister_dev(struct hci_dev *hdev);
+void hci_unregister_dev(struct hci_dev *hdev);
 int hci_suspend_dev(struct hci_dev *hdev);
 int hci_resume_dev(struct hci_dev *hdev);
 int hci_dev_open(__u16 dev);
@@ -597,8 +656,9 @@ int hci_recv_frame(struct sk_buff *skb);
 int hci_recv_fragment(struct hci_dev *hdev, int type, void *data, int count);
 int hci_recv_stream_fragment(struct hci_dev *hdev, void *data, int count);
 
-int hci_register_sysfs(struct hci_dev *hdev);
-void hci_unregister_sysfs(struct hci_dev *hdev);
+void hci_init_sysfs(struct hci_dev *hdev);
+int hci_add_sysfs(struct hci_dev *hdev);
+void hci_del_sysfs(struct hci_dev *hdev);
 void hci_conn_init_sysfs(struct hci_conn *conn);
 void hci_conn_add_sysfs(struct hci_conn *conn);
 void hci_conn_del_sysfs(struct hci_conn *conn);
@@ -674,7 +734,7 @@ static inline void hci_proto_connect_cfm(struct hci_conn *conn, __u8 status)
 static inline int hci_proto_disconn_ind(struct hci_conn *conn)
 {
        register struct hci_proto *hp;
-       int reason = 0x13;
+       int reason = HCI_ERROR_REMOTE_USER_TERM;
 
        hp = hci_proto[HCI_PROTO_L2CAP];
        if (hp && hp->disconn_ind)
@@ -834,7 +894,7 @@ int hci_register_notifier(struct notifier_block *nb);
 int hci_unregister_notifier(struct notifier_block *nb);
 
 int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param);
-void hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags);
+void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags);
 void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb);
 
 void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode);
@@ -847,34 +907,41 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb,
 
 /* Management interface */
 int mgmt_control(struct sock *sk, struct msghdr *msg, size_t len);
-int mgmt_index_added(u16 index);
-int mgmt_index_removed(u16 index);
-int mgmt_powered(u16 index, u8 powered);
-int mgmt_discoverable(u16 index, u8 discoverable);
-int mgmt_connectable(u16 index, u8 connectable);
-int mgmt_new_key(u16 index, struct link_key *key, u8 persistent);
-int mgmt_connected(u16 index, bdaddr_t *bdaddr, u8 link_type);
-int mgmt_disconnected(u16 index, bdaddr_t *bdaddr);
-int mgmt_disconnect_failed(u16 index);
-int mgmt_connect_failed(u16 index, bdaddr_t *bdaddr, u8 status);
-int mgmt_pin_code_request(u16 index, bdaddr_t *bdaddr, u8 secure);
-int mgmt_pin_code_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status);
-int mgmt_pin_code_neg_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status);
-int mgmt_user_confirm_request(u16 index, bdaddr_t *bdaddr, __le32 value,
-                                                       u8 confirm_hint);
-int mgmt_user_confirm_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status);
-int mgmt_user_confirm_neg_reply_complete(u16 index, bdaddr_t *bdaddr,
+int mgmt_index_added(struct hci_dev *hdev);
+int mgmt_index_removed(struct hci_dev *hdev);
+int mgmt_powered(struct hci_dev *hdev, u8 powered);
+int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable);
+int mgmt_connectable(struct hci_dev *hdev, u8 connectable);
+int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status);
+int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
+                                                               u8 persistent);
+int mgmt_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
+int mgmt_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
+int mgmt_disconnect_failed(struct hci_dev *hdev);
+int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type,
                                                                u8 status);
-int mgmt_auth_failed(u16 index, bdaddr_t *bdaddr, u8 status);
-int mgmt_set_local_name_complete(u16 index, u8 *name, u8 status);
-int mgmt_read_local_oob_data_reply_complete(u16 index, u8 *hash, u8 *randomizer,
+int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure);
+int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
                                                                u8 status);
-int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 *dev_class, s8 rssi,
-                                                               u8 *eir);
-int mgmt_remote_name(u16 index, bdaddr_t *bdaddr, u8 *name);
-int mgmt_discovering(u16 index, u8 discovering);
-int mgmt_device_blocked(u16 index, bdaddr_t *bdaddr);
-int mgmt_device_unblocked(u16 index, bdaddr_t *bdaddr);
+int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                                                               u8 status);
+int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                                               __le32 value, u8 confirm_hint);
+int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                                                               u8 status);
+int mgmt_user_confirm_neg_reply_complete(struct hci_dev *hdev,
+                                               bdaddr_t *bdaddr, u8 status);
+int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 status);
+int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status);
+int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
+                                               u8 *randomizer, u8 status);
+int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type,
+                                       u8 *dev_class, s8 rssi, u8 *eir);
+int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *name);
+int mgmt_inquiry_failed(struct hci_dev *hdev, u8 status);
+int mgmt_discovering(struct hci_dev *hdev, u8 discovering);
+int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr);
+int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr);
 
 /* HCI info for socket */
 #define hci_pi(sk) ((struct hci_pinfo *) sk)
@@ -913,4 +980,7 @@ void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8],
 void hci_le_ltk_reply(struct hci_conn *conn, u8 ltk[16]);
 void hci_le_ltk_neg_reply(struct hci_conn *conn);
 
+int hci_do_inquiry(struct hci_dev *hdev, u8 length);
+int hci_cancel_inquiry(struct hci_dev *hdev);
+
 #endif /* __HCI_CORE_H */
index 6cc18f37167519260e15d1ad20d7bf2f145c86c5..875021ad0675098e6e9bb5cfa1cfc5dea170bcad 100644 (file)
 #ifndef __L2CAP_H
 #define __L2CAP_H
 
+#include <asm/unaligned.h>
+
 /* L2CAP defaults */
 #define L2CAP_DEFAULT_MTU              672
 #define L2CAP_DEFAULT_MIN_MTU          48
 #define L2CAP_DEFAULT_FLUSH_TO         0xffff
 #define L2CAP_DEFAULT_TX_WINDOW                63
+#define L2CAP_DEFAULT_EXT_WINDOW       0x3FFF
 #define L2CAP_DEFAULT_MAX_TX           3
 #define L2CAP_DEFAULT_RETRANS_TO       2000    /* 2 seconds */
 #define L2CAP_DEFAULT_MONITOR_TO       12000   /* 12 seconds */
 #define L2CAP_DEFAULT_MAX_PDU_SIZE     1009    /* Sized for 3-DH5 packet */
 #define L2CAP_DEFAULT_ACK_TO           200
 #define L2CAP_LE_DEFAULT_MTU           23
+#define L2CAP_DEFAULT_MAX_SDU_SIZE     0xFFFF
+#define L2CAP_DEFAULT_SDU_ITIME                0xFFFFFFFF
+#define L2CAP_DEFAULT_ACC_LAT          0xFFFFFFFF
 
 #define L2CAP_DISC_TIMEOUT             (100)
 #define L2CAP_DISC_REJ_TIMEOUT         (5000)  /*  5 seconds */
@@ -91,52 +97,82 @@ struct l2cap_conninfo {
 #define L2CAP_ECHO_RSP         0x09
 #define L2CAP_INFO_REQ         0x0a
 #define L2CAP_INFO_RSP         0x0b
+#define L2CAP_CREATE_CHAN_REQ  0x0c
+#define L2CAP_CREATE_CHAN_RSP  0x0d
+#define L2CAP_MOVE_CHAN_REQ    0x0e
+#define L2CAP_MOVE_CHAN_RSP    0x0f
+#define L2CAP_MOVE_CHAN_CFM    0x10
+#define L2CAP_MOVE_CHAN_CFM_RSP        0x11
 #define L2CAP_CONN_PARAM_UPDATE_REQ    0x12
 #define L2CAP_CONN_PARAM_UPDATE_RSP    0x13
 
-/* L2CAP feature mask */
+/* L2CAP extended feature mask */
 #define L2CAP_FEAT_FLOWCTL     0x00000001
 #define L2CAP_FEAT_RETRANS     0x00000002
+#define L2CAP_FEAT_BIDIR_QOS   0x00000004
 #define L2CAP_FEAT_ERTM                0x00000008
 #define L2CAP_FEAT_STREAMING   0x00000010
 #define L2CAP_FEAT_FCS         0x00000020
+#define L2CAP_FEAT_EXT_FLOW    0x00000040
 #define L2CAP_FEAT_FIXED_CHAN  0x00000080
+#define L2CAP_FEAT_EXT_WINDOW  0x00000100
+#define L2CAP_FEAT_UCD         0x00000200
 
 /* L2CAP checksum option */
 #define L2CAP_FCS_NONE         0x00
 #define L2CAP_FCS_CRC16                0x01
 
+/* L2CAP fixed channels */
+#define L2CAP_FC_L2CAP         0x02
+#define L2CAP_FC_A2MP          0x08
+
 /* L2CAP Control Field bit masks */
-#define L2CAP_CTRL_SAR               0xC000
-#define L2CAP_CTRL_REQSEQ            0x3F00
-#define L2CAP_CTRL_TXSEQ             0x007E
-#define L2CAP_CTRL_RETRANS           0x0080
-#define L2CAP_CTRL_FINAL             0x0080
-#define L2CAP_CTRL_POLL              0x0010
-#define L2CAP_CTRL_SUPERVISE         0x000C
-#define L2CAP_CTRL_FRAME_TYPE        0x0001 /* I- or S-Frame */
-
-#define L2CAP_CTRL_TXSEQ_SHIFT      1
-#define L2CAP_CTRL_REQSEQ_SHIFT     8
-#define L2CAP_CTRL_SAR_SHIFT       14
+#define L2CAP_CTRL_SAR                 0xC000
+#define L2CAP_CTRL_REQSEQ              0x3F00
+#define L2CAP_CTRL_TXSEQ               0x007E
+#define L2CAP_CTRL_SUPERVISE           0x000C
+
+#define L2CAP_CTRL_RETRANS             0x0080
+#define L2CAP_CTRL_FINAL               0x0080
+#define L2CAP_CTRL_POLL                        0x0010
+#define L2CAP_CTRL_FRAME_TYPE          0x0001 /* I- or S-Frame */
+
+#define L2CAP_CTRL_TXSEQ_SHIFT         1
+#define L2CAP_CTRL_SUPER_SHIFT         2
+#define L2CAP_CTRL_REQSEQ_SHIFT                8
+#define L2CAP_CTRL_SAR_SHIFT           14
+
+/* L2CAP Extended Control Field bit mask */
+#define L2CAP_EXT_CTRL_TXSEQ           0xFFFC0000
+#define L2CAP_EXT_CTRL_SAR             0x00030000
+#define L2CAP_EXT_CTRL_SUPERVISE       0x00030000
+#define L2CAP_EXT_CTRL_REQSEQ          0x0000FFFC
+
+#define L2CAP_EXT_CTRL_POLL            0x00040000
+#define L2CAP_EXT_CTRL_FINAL           0x00000002
+#define L2CAP_EXT_CTRL_FRAME_TYPE      0x00000001 /* I- or S-Frame */
+
+#define L2CAP_EXT_CTRL_REQSEQ_SHIFT    2
+#define L2CAP_EXT_CTRL_SAR_SHIFT       16
+#define L2CAP_EXT_CTRL_SUPER_SHIFT     16
+#define L2CAP_EXT_CTRL_TXSEQ_SHIFT     18
 
 /* L2CAP Supervisory Function */
-#define L2CAP_SUPER_RCV_READY           0x0000
-#define L2CAP_SUPER_REJECT              0x0004
-#define L2CAP_SUPER_RCV_NOT_READY       0x0008
-#define L2CAP_SUPER_SELECT_REJECT       0x000C
+#define L2CAP_SUPER_RR         0x00
+#define L2CAP_SUPER_REJ                0x01
+#define L2CAP_SUPER_RNR                0x02
+#define L2CAP_SUPER_SREJ       0x03
 
 /* L2CAP Segmentation and Reassembly */
-#define L2CAP_SDU_UNSEGMENTED       0x0000
-#define L2CAP_SDU_START             0x4000
-#define L2CAP_SDU_END               0x8000
-#define L2CAP_SDU_CONTINUE          0xC000
+#define L2CAP_SAR_UNSEGMENTED  0x00
+#define L2CAP_SAR_START                0x01
+#define L2CAP_SAR_END          0x02
+#define L2CAP_SAR_CONTINUE     0x03
 
 /* L2CAP Command rej. reasons */
-#define L2CAP_REJ_NOT_UNDERSTOOD      0x0000
-#define L2CAP_REJ_MTU_EXCEEDED        0x0001
-#define L2CAP_REJ_INVALID_CID         0x0002
-
+#define L2CAP_REJ_NOT_UNDERSTOOD       0x0000
+#define L2CAP_REJ_MTU_EXCEEDED         0x0001
+#define L2CAP_REJ_INVALID_CID          0x0002
 
 /* L2CAP structures */
 struct l2cap_hdr {
@@ -144,6 +180,12 @@ struct l2cap_hdr {
        __le16     cid;
 } __packed;
 #define L2CAP_HDR_SIZE         4
+#define L2CAP_ENH_HDR_SIZE     6
+#define L2CAP_EXT_HDR_SIZE     8
+
+#define L2CAP_FCS_SIZE         2
+#define L2CAP_SDULEN_SIZE      2
+#define L2CAP_PSMLEN_SIZE      2
 
 struct l2cap_cmd_hdr {
        __u8       code;
@@ -188,14 +230,15 @@ struct l2cap_conn_rsp {
 #define L2CAP_CID_DYN_START    0x0040
 #define L2CAP_CID_DYN_END      0xffff
 
-/* connect result */
+/* connect/create channel results */
 #define L2CAP_CR_SUCCESS       0x0000
 #define L2CAP_CR_PEND          0x0001
 #define L2CAP_CR_BAD_PSM       0x0002
 #define L2CAP_CR_SEC_BLOCK     0x0003
 #define L2CAP_CR_NO_MEM                0x0004
+#define L2CAP_CR_BAD_AMP       0x0005
 
-/* connect status */
+/* connect/create channel status */
 #define L2CAP_CS_NO_INFO       0x0000
 #define L2CAP_CS_AUTHEN_PEND   0x0001
 #define L2CAP_CS_AUTHOR_PEND   0x0002
@@ -217,6 +260,8 @@ struct l2cap_conf_rsp {
 #define L2CAP_CONF_UNACCEPT    0x0001
 #define L2CAP_CONF_REJECT      0x0002
 #define L2CAP_CONF_UNKNOWN     0x0003
+#define L2CAP_CONF_PENDING     0x0004
+#define L2CAP_CONF_EFS_REJECT  0x0005
 
 struct l2cap_conf_opt {
        __u8       type;
@@ -233,6 +278,8 @@ struct l2cap_conf_opt {
 #define L2CAP_CONF_QOS         0x03
 #define L2CAP_CONF_RFC         0x04
 #define L2CAP_CONF_FCS         0x05
+#define L2CAP_CONF_EFS         0x06
+#define L2CAP_CONF_EWS         0x07
 
 #define L2CAP_CONF_MAX_SIZE    22
 
@@ -251,6 +298,21 @@ struct l2cap_conf_rfc {
 #define L2CAP_MODE_ERTM                0x03
 #define L2CAP_MODE_STREAMING   0x04
 
+struct l2cap_conf_efs {
+       __u8    id;
+       __u8    stype;
+       __le16  msdu;
+       __le32  sdu_itime;
+       __le32  acc_lat;
+       __le32  flush_to;
+} __packed;
+
+#define L2CAP_SERV_NOTRAFIC    0x00
+#define L2CAP_SERV_BESTEFFORT  0x01
+#define L2CAP_SERV_GUARANTEED  0x02
+
+#define L2CAP_BESTEFFORT_ID    0x01
+
 struct l2cap_disconn_req {
        __le16     dcid;
        __le16     scid;
@@ -271,14 +333,57 @@ struct l2cap_info_rsp {
        __u8        data[0];
 } __packed;
 
+struct l2cap_create_chan_req {
+       __le16      psm;
+       __le16      scid;
+       __u8        amp_id;
+} __packed;
+
+struct l2cap_create_chan_rsp {
+       __le16      dcid;
+       __le16      scid;
+       __le16      result;
+       __le16      status;
+} __packed;
+
+struct l2cap_move_chan_req {
+       __le16      icid;
+       __u8        dest_amp_id;
+} __packed;
+
+struct l2cap_move_chan_rsp {
+       __le16      icid;
+       __le16      result;
+} __packed;
+
+#define L2CAP_MR_SUCCESS       0x0000
+#define L2CAP_MR_PEND          0x0001
+#define L2CAP_MR_BAD_ID                0x0002
+#define L2CAP_MR_SAME_ID       0x0003
+#define L2CAP_MR_NOT_SUPP      0x0004
+#define L2CAP_MR_COLLISION     0x0005
+#define L2CAP_MR_NOT_ALLOWED   0x0006
+
+struct l2cap_move_chan_cfm {
+       __le16      icid;
+       __le16      result;
+} __packed;
+
+#define L2CAP_MC_CONFIRMED     0x0000
+#define L2CAP_MC_UNCONFIRMED   0x0001
+
+struct l2cap_move_chan_cfm_rsp {
+       __le16      icid;
+} __packed;
+
 /* info type */
-#define L2CAP_IT_CL_MTU     0x0001
-#define L2CAP_IT_FEAT_MASK  0x0002
-#define L2CAP_IT_FIXED_CHAN 0x0003
+#define L2CAP_IT_CL_MTU                0x0001
+#define L2CAP_IT_FEAT_MASK     0x0002
+#define L2CAP_IT_FIXED_CHAN    0x0003
 
 /* info result */
-#define L2CAP_IR_SUCCESS    0x0000
-#define L2CAP_IR_NOTSUPP    0x0001
+#define L2CAP_IR_SUCCESS       0x0000
+#define L2CAP_IR_NOTSUPP       0x0001
 
 struct l2cap_conn_param_update_req {
        __le16      min;
@@ -297,7 +402,7 @@ struct l2cap_conn_param_update_rsp {
 
 /* ----- L2CAP channels and connections ----- */
 struct srej_list {
-       __u   tx_seq;
+       __u16   tx_seq;
        struct list_head list;
 };
 
@@ -319,14 +424,11 @@ struct l2cap_chan {
        __u16           flush_to;
        __u8            mode;
        __u8            chan_type;
+       __u8            chan_policy;
 
        __le16          sport;
 
        __u8            sec_level;
-       __u8            role_switch;
-       __u8            force_reliable;
-       __u8            flushable;
-       __u8            force_active;
 
        __u8            ident;
 
@@ -337,7 +439,8 @@ struct l2cap_chan {
 
        __u8            fcs;
 
-       __u8            tx_win;
+       __u16           tx_win;
+       __u16           tx_win_max;
        __u8            max_tx;
        __u16           retrans_timeout;
        __u16           monitor_timeout;
@@ -345,25 +448,40 @@ struct l2cap_chan {
 
        unsigned long   conf_state;
        unsigned long   conn_state;
-
-       __u8            next_tx_seq;
-       __u8            expected_ack_seq;
-       __u8            expected_tx_seq;
-       __u8            buffer_seq;
-       __u8            buffer_seq_srej;
-       __u8            srej_save_reqseq;
-       __u8            frames_sent;
-       __u8            unacked_frames;
+       unsigned long   flags;
+
+       __u16           next_tx_seq;
+       __u16           expected_ack_seq;
+       __u16           expected_tx_seq;
+       __u16           buffer_seq;
+       __u16           buffer_seq_srej;
+       __u16           srej_save_reqseq;
+       __u16           frames_sent;
+       __u16           unacked_frames;
        __u8            retry_count;
        __u8            num_acked;
        __u16           sdu_len;
        struct sk_buff  *sdu;
        struct sk_buff  *sdu_last_frag;
 
-       __u           remote_tx_win;
+       __u16           remote_tx_win;
        __u8            remote_max_tx;
        __u16           remote_mps;
 
+       __u8            local_id;
+       __u8            local_stype;
+       __u16           local_msdu;
+       __u32           local_sdu_itime;
+       __u32           local_acc_lat;
+       __u32           local_flush_to;
+
+       __u8            remote_id;
+       __u8            remote_stype;
+       __u16           remote_msdu;
+       __u32           remote_sdu_itime;
+       __u32           remote_acc_lat;
+       __u32           remote_flush_to;
+
        struct timer_list       chan_timer;
        struct timer_list       retrans_timer;
        struct timer_list       monitor_timer;
@@ -391,6 +509,7 @@ struct l2cap_ops {
 
 struct l2cap_conn {
        struct hci_conn *hcon;
+       struct hci_chan *hchan;
 
        bdaddr_t        *dst;
        bdaddr_t        *src;
@@ -445,6 +564,9 @@ enum {
        CONF_CONNECT_PEND,
        CONF_NO_FCS_RECV,
        CONF_STATE2_DEVICE,
+       CONF_EWS_RECV,
+       CONF_LOC_CONF_PEND,
+       CONF_REM_CONF_PEND,
 };
 
 #define L2CAP_CONF_MAX_CONF_REQ 2
@@ -462,6 +584,16 @@ enum {
        CONN_RNR_SENT,
 };
 
+/* Definitions for flags in l2cap_chan */
+enum {
+       FLAG_ROLE_SWITCH,
+       FLAG_FORCE_ACTIVE,
+       FLAG_FORCE_RELIABLE,
+       FLAG_FLUSHABLE,
+       FLAG_EXT_CTRL,
+       FLAG_EFS_ENABLE,
+};
+
 #define __set_chan_timer(c, t) l2cap_set_timer(c, &c->chan_timer, (t))
 #define __clear_chan_timer(c) l2cap_clear_timer(c, &c->chan_timer)
 #define __set_retrans_timer(c) l2cap_set_timer(c, &c->retrans_timer, \
@@ -474,6 +606,22 @@ enum {
                L2CAP_DEFAULT_ACK_TO);
 #define __clear_ack_timer(c) l2cap_clear_timer(c, &c->ack_timer)
 
+static inline int __seq_offset(struct l2cap_chan *chan, __u16 seq1, __u16 seq2)
+{
+       int offset;
+
+       offset = (seq1 - seq2) % (chan->tx_win_max + 1);
+       if (offset < 0)
+               offset += (chan->tx_win_max + 1);
+
+       return offset;
+}
+
+static inline __u16 __next_seq(struct l2cap_chan *chan, __u16 seq)
+{
+       return (seq + 1) % (chan->tx_win_max + 1);
+}
+
 static inline int l2cap_tx_window_full(struct l2cap_chan *ch)
 {
        int sub;
@@ -486,13 +634,165 @@ static inline int l2cap_tx_window_full(struct l2cap_chan *ch)
        return sub == ch->remote_tx_win;
 }
 
-#define __get_txseq(ctrl)      (((ctrl) & L2CAP_CTRL_TXSEQ) >> 1)
-#define __get_reqseq(ctrl)     (((ctrl) & L2CAP_CTRL_REQSEQ) >> 8)
-#define __is_iframe(ctrl)      (!((ctrl) & L2CAP_CTRL_FRAME_TYPE))
-#define __is_sframe(ctrl)      ((ctrl) & L2CAP_CTRL_FRAME_TYPE)
-#define __is_sar_start(ctrl)   (((ctrl) & L2CAP_CTRL_SAR) == L2CAP_SDU_START)
+static inline __u16 __get_reqseq(struct l2cap_chan *chan, __u32 ctrl)
+{
+       if (test_bit(FLAG_EXT_CTRL, &chan->flags))
+               return (ctrl & L2CAP_EXT_CTRL_REQSEQ) >>
+                                               L2CAP_EXT_CTRL_REQSEQ_SHIFT;
+       else
+               return (ctrl & L2CAP_CTRL_REQSEQ) >> L2CAP_CTRL_REQSEQ_SHIFT;
+}
+
+static inline __u32 __set_reqseq(struct l2cap_chan *chan, __u32 reqseq)
+{
+       if (test_bit(FLAG_EXT_CTRL, &chan->flags))
+               return (reqseq << L2CAP_EXT_CTRL_REQSEQ_SHIFT) &
+                                                       L2CAP_EXT_CTRL_REQSEQ;
+       else
+               return (reqseq << L2CAP_CTRL_REQSEQ_SHIFT) & L2CAP_CTRL_REQSEQ;
+}
+
+static inline __u16 __get_txseq(struct l2cap_chan *chan, __u32 ctrl)
+{
+       if (test_bit(FLAG_EXT_CTRL, &chan->flags))
+               return (ctrl & L2CAP_EXT_CTRL_TXSEQ) >>
+                                               L2CAP_EXT_CTRL_TXSEQ_SHIFT;
+       else
+               return (ctrl & L2CAP_CTRL_TXSEQ) >> L2CAP_CTRL_TXSEQ_SHIFT;
+}
+
+static inline __u32 __set_txseq(struct l2cap_chan *chan, __u32 txseq)
+{
+       if (test_bit(FLAG_EXT_CTRL, &chan->flags))
+               return (txseq << L2CAP_EXT_CTRL_TXSEQ_SHIFT) &
+                                                       L2CAP_EXT_CTRL_TXSEQ;
+       else
+               return (txseq << L2CAP_CTRL_TXSEQ_SHIFT) & L2CAP_CTRL_TXSEQ;
+}
+
+static inline bool __is_sframe(struct l2cap_chan *chan, __u32 ctrl)
+{
+       if (test_bit(FLAG_EXT_CTRL, &chan->flags))
+               return ctrl & L2CAP_EXT_CTRL_FRAME_TYPE;
+       else
+               return ctrl & L2CAP_CTRL_FRAME_TYPE;
+}
+
+static inline __u32 __set_sframe(struct l2cap_chan *chan)
+{
+       if (test_bit(FLAG_EXT_CTRL, &chan->flags))
+               return L2CAP_EXT_CTRL_FRAME_TYPE;
+       else
+               return L2CAP_CTRL_FRAME_TYPE;
+}
+
+static inline __u8 __get_ctrl_sar(struct l2cap_chan *chan, __u32 ctrl)
+{
+       if (test_bit(FLAG_EXT_CTRL, &chan->flags))
+               return (ctrl & L2CAP_EXT_CTRL_SAR) >> L2CAP_EXT_CTRL_SAR_SHIFT;
+       else
+               return (ctrl & L2CAP_CTRL_SAR) >> L2CAP_CTRL_SAR_SHIFT;
+}
+
+static inline __u32 __set_ctrl_sar(struct l2cap_chan *chan, __u32 sar)
+{
+       if (test_bit(FLAG_EXT_CTRL, &chan->flags))
+               return (sar << L2CAP_EXT_CTRL_SAR_SHIFT) & L2CAP_EXT_CTRL_SAR;
+       else
+               return (sar << L2CAP_CTRL_SAR_SHIFT) & L2CAP_CTRL_SAR;
+}
+
+static inline bool __is_sar_start(struct l2cap_chan *chan, __u32 ctrl)
+{
+       return __get_ctrl_sar(chan, ctrl) == L2CAP_SAR_START;
+}
+
+static inline __u32 __get_sar_mask(struct l2cap_chan *chan)
+{
+       if (test_bit(FLAG_EXT_CTRL, &chan->flags))
+               return L2CAP_EXT_CTRL_SAR;
+       else
+               return L2CAP_CTRL_SAR;
+}
+
+static inline __u8 __get_ctrl_super(struct l2cap_chan *chan, __u32 ctrl)
+{
+       if (test_bit(FLAG_EXT_CTRL, &chan->flags))
+               return (ctrl & L2CAP_EXT_CTRL_SUPERVISE) >>
+                                               L2CAP_EXT_CTRL_SUPER_SHIFT;
+       else
+               return (ctrl & L2CAP_CTRL_SUPERVISE) >> L2CAP_CTRL_SUPER_SHIFT;
+}
+
+static inline __u32 __set_ctrl_super(struct l2cap_chan *chan, __u32 super)
+{
+       if (test_bit(FLAG_EXT_CTRL, &chan->flags))
+               return (super << L2CAP_EXT_CTRL_SUPER_SHIFT) &
+                                               L2CAP_EXT_CTRL_SUPERVISE;
+       else
+               return (super << L2CAP_CTRL_SUPER_SHIFT) &
+                                                       L2CAP_CTRL_SUPERVISE;
+}
+
+static inline __u32 __set_ctrl_final(struct l2cap_chan *chan)
+{
+       if (test_bit(FLAG_EXT_CTRL, &chan->flags))
+               return L2CAP_EXT_CTRL_FINAL;
+       else
+               return L2CAP_CTRL_FINAL;
+}
+
+static inline bool __is_ctrl_final(struct l2cap_chan *chan, __u32 ctrl)
+{
+       if (test_bit(FLAG_EXT_CTRL, &chan->flags))
+               return ctrl & L2CAP_EXT_CTRL_FINAL;
+       else
+               return ctrl & L2CAP_CTRL_FINAL;
+}
+
+static inline __u32 __set_ctrl_poll(struct l2cap_chan *chan)
+{
+       if (test_bit(FLAG_EXT_CTRL, &chan->flags))
+               return L2CAP_EXT_CTRL_POLL;
+       else
+               return L2CAP_CTRL_POLL;
+}
+
+static inline bool __is_ctrl_poll(struct l2cap_chan *chan, __u32 ctrl)
+{
+       if (test_bit(FLAG_EXT_CTRL, &chan->flags))
+               return ctrl & L2CAP_EXT_CTRL_POLL;
+       else
+               return ctrl & L2CAP_CTRL_POLL;
+}
+
+static inline __u32 __get_control(struct l2cap_chan *chan, void *p)
+{
+       if (test_bit(FLAG_EXT_CTRL, &chan->flags))
+               return get_unaligned_le32(p);
+       else
+               return get_unaligned_le16(p);
+}
+
+static inline void __put_control(struct l2cap_chan *chan, __u32 control,
+                                                               void *p)
+{
+       if (test_bit(FLAG_EXT_CTRL, &chan->flags))
+               return put_unaligned_le32(control, p);
+       else
+               return put_unaligned_le16(control, p);
+}
+
+static inline __u8 __ctrl_size(struct l2cap_chan *chan)
+{
+       if (test_bit(FLAG_EXT_CTRL, &chan->flags))
+               return L2CAP_EXT_HDR_SIZE - L2CAP_HDR_SIZE;
+       else
+               return L2CAP_ENH_HDR_SIZE - L2CAP_HDR_SIZE;
+}
 
 extern int disable_ertm;
+extern int enable_hs;
 
 int l2cap_init_sockets(void);
 void l2cap_cleanup_sockets(void);
@@ -507,7 +807,8 @@ struct l2cap_chan *l2cap_chan_create(struct sock *sk);
 void l2cap_chan_close(struct l2cap_chan *chan, int reason);
 void l2cap_chan_destroy(struct l2cap_chan *chan);
 int l2cap_chan_connect(struct l2cap_chan *chan);
-int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len);
+int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
+                                                               u32 priority);
 void l2cap_chan_busy(struct l2cap_chan *chan, int busy);
 
 #endif /* __L2CAP_H */
index d66da0f94f95ae60e4ceb1d2fe713bc90c376e08..3e320c9cae8ffc7238d1c1197dc45c828a90931b 100644 (file)
@@ -69,6 +69,10 @@ struct mgmt_mode {
 #define MGMT_OP_SET_POWERED            0x0005
 
 #define MGMT_OP_SET_DISCOVERABLE       0x0006
+struct mgmt_cp_set_discoverable {
+       __u8 val;
+       __u16 timeout;
+} __packed;
 
 #define MGMT_OP_SET_CONNECTABLE                0x0007
 
@@ -96,24 +100,22 @@ struct mgmt_cp_set_service_cache {
        __u8 enable;
 } __packed;
 
-struct mgmt_key_info {
+struct mgmt_link_key_info {
        bdaddr_t bdaddr;
        u8 type;
        u8 val[16];
        u8 pin_len;
-       u8 dlen;
-       u8 data[0];
 } __packed;
 
-#define MGMT_OP_LOAD_KEYS              0x000D
-struct mgmt_cp_load_keys {
+#define MGMT_OP_LOAD_LINK_KEYS         0x000D
+struct mgmt_cp_load_link_keys {
        __u8 debug_keys;
        __le16 key_count;
-       struct mgmt_key_info keys[0];
+       struct mgmt_link_key_info keys[0];
 } __packed;
 
-#define MGMT_OP_REMOVE_KEY             0x000E
-struct mgmt_cp_remove_key {
+#define MGMT_OP_REMOVE_KEYS            0x000E
+struct mgmt_cp_remove_keys {
        bdaddr_t bdaddr;
        __u8 disconnect;
 } __packed;
@@ -126,10 +128,20 @@ struct mgmt_rp_disconnect {
        bdaddr_t bdaddr;
 } __packed;
 
+#define MGMT_ADDR_BREDR                        0x00
+#define MGMT_ADDR_LE                   0x01
+#define MGMT_ADDR_BREDR_LE             0x02
+#define MGMT_ADDR_INVALID              0xff
+
+struct mgmt_addr_info {
+       bdaddr_t bdaddr;
+       __u8 type;
+} __packed;
+
 #define MGMT_OP_GET_CONNECTIONS                0x0010
 struct mgmt_rp_get_connections {
        __le16 conn_count;
-       bdaddr_t conn[0];
+       struct mgmt_addr_info addr[0];
 } __packed;
 
 #define MGMT_OP_PIN_CODE_REPLY         0x0011
@@ -245,26 +257,19 @@ struct mgmt_ev_controller_error {
 
 #define MGMT_EV_PAIRABLE               0x0009
 
-#define MGMT_EV_NEW_KEY                        0x000A
-struct mgmt_ev_new_key {
+#define MGMT_EV_NEW_LINK_KEY           0x000A
+struct mgmt_ev_new_link_key {
        __u8 store_hint;
-       struct mgmt_key_info key;
+       struct mgmt_link_key_info key;
 } __packed;
 
 #define MGMT_EV_CONNECTED              0x000B
-struct mgmt_ev_connected {
-       bdaddr_t bdaddr;
-       __u8 link_type;
-} __packed;
 
 #define MGMT_EV_DISCONNECTED           0x000C
-struct mgmt_ev_disconnected {
-       bdaddr_t bdaddr;
-} __packed;
 
 #define MGMT_EV_CONNECT_FAILED         0x000D
 struct mgmt_ev_connect_failed {
-       bdaddr_t bdaddr;
+       struct mgmt_addr_info addr;
        __u8 status;
 } __packed;
 
@@ -294,7 +299,7 @@ struct mgmt_ev_local_name_changed {
 
 #define MGMT_EV_DEVICE_FOUND           0x0012
 struct mgmt_ev_device_found {
-       bdaddr_t bdaddr;
+       struct mgmt_addr_info addr;
        __u8 dev_class[3];
        __s8 rssi;
        __u8 eir[HCI_MAX_EIR_LENGTH];
index 91bcd3a961ec22c501451ef588fe86b171c55ae3..a6cd856046ab247cb29da2d9831077153da8a642 100644 (file)
@@ -65,15 +65,13 @@ static DECLARE_RWSEM(bnep_session_sem);
 static struct bnep_session *__bnep_get_session(u8 *dst)
 {
        struct bnep_session *s;
-       struct list_head *p;
 
        BT_DBG("");
 
-       list_for_each(p, &bnep_session_list) {
-               s = list_entry(p, struct bnep_session, list);
+       list_for_each_entry(s, &bnep_session_list, list)
                if (!compare_ether_addr(dst, s->eh.h_source))
                        return s;
-       }
+
        return NULL;
 }
 
@@ -667,17 +665,14 @@ static void __bnep_copy_ci(struct bnep_conninfo *ci, struct bnep_session *s)
 
 int bnep_get_connlist(struct bnep_connlist_req *req)
 {
-       struct list_head *p;
+       struct bnep_session *s;
        int err = 0, n = 0;
 
        down_read(&bnep_session_sem);
 
-       list_for_each(p, &bnep_session_list) {
-               struct bnep_session *s;
+       list_for_each_entry(s, &bnep_session_list, list) {
                struct bnep_conninfo ci;
 
-               s = list_entry(p, struct bnep_session, list);
-
                __bnep_copy_ci(&ci, s);
 
                if (copy_to_user(req->ci, &ci, sizeof(ci))) {
index 7d00ddf9e9dcb55ab74cf768db31a3e15af647ac..9e8940b24bbad73c67dd37fd23b0e787090cacd8 100644 (file)
@@ -53,15 +53,13 @@ static LIST_HEAD(cmtp_session_list);
 static struct cmtp_session *__cmtp_get_session(bdaddr_t *bdaddr)
 {
        struct cmtp_session *session;
-       struct list_head *p;
 
        BT_DBG("");
 
-       list_for_each(p, &cmtp_session_list) {
-               session = list_entry(p, struct cmtp_session, list);
+       list_for_each_entry(session, &cmtp_session_list, list)
                if (!bacmp(bdaddr, &session->bdaddr))
                        return session;
-       }
+
        return NULL;
 }
 
@@ -431,19 +429,16 @@ int cmtp_del_connection(struct cmtp_conndel_req *req)
 
 int cmtp_get_connlist(struct cmtp_connlist_req *req)
 {
-       struct list_head *p;
+       struct cmtp_session *session;
        int err = 0, n = 0;
 
        BT_DBG("");
 
        down_read(&cmtp_session_sem);
 
-       list_for_each(p, &cmtp_session_list) {
-               struct cmtp_session *session;
+       list_for_each_entry(session, &cmtp_session_list, list) {
                struct cmtp_conninfo ci;
 
-               session = list_entry(p, struct cmtp_session, list);
-
                __cmtp_copy_session(session, &ci);
 
                if (copy_to_user(req->ci, &ci, sizeof(ci))) {
index e0af7237cd9245fedfc8886ec446f670ddc48500..de0b93e45980138235044550be69f960cd8cf9ab 100644 (file)
@@ -374,6 +374,8 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
 
        skb_queue_head_init(&conn->data_q);
 
+       hci_chan_hash_init(conn);
+
        setup_timer(&conn->disc_timer, hci_conn_timeout, (unsigned long)conn);
        setup_timer(&conn->idle_timer, hci_conn_idle, (unsigned long)conn);
        setup_timer(&conn->auto_accept_timer, hci_conn_auto_accept,
@@ -432,6 +434,8 @@ int hci_conn_del(struct hci_conn *conn)
 
        tasklet_disable(&hdev->tx_task);
 
+       hci_chan_hash_flush(conn);
+
        hci_conn_hash_del(hdev, conn);
        if (hdev->notify)
                hdev->notify(hdev, HCI_NOTIFY_CONN_DEL);
@@ -453,16 +457,13 @@ int hci_conn_del(struct hci_conn *conn)
 struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src)
 {
        int use_src = bacmp(src, BDADDR_ANY);
-       struct hci_dev *hdev = NULL;
-       struct list_head *p;
+       struct hci_dev *hdev = NULL, *d;
 
        BT_DBG("%s -> %s", batostr(src), batostr(dst));
 
        read_lock_bh(&hci_dev_list_lock);
 
-       list_for_each(p, &hci_dev_list) {
-               struct hci_dev *d = list_entry(p, struct hci_dev, list);
-
+       list_for_each_entry(d, &hci_dev_list, list) {
                if (!test_bit(HCI_UP, &d->flags) || test_bit(HCI_RAW, &d->flags))
                        continue;
 
@@ -819,7 +820,7 @@ void hci_conn_hash_flush(struct hci_dev *hdev)
 
                c->state = BT_CLOSED;
 
-               hci_proto_disconn_cfm(c, 0x16);
+               hci_proto_disconn_cfm(c, HCI_ERROR_LOCAL_HOST_TERM);
                hci_conn_del(c);
        }
 }
@@ -855,10 +856,10 @@ EXPORT_SYMBOL(hci_conn_put_device);
 
 int hci_get_conn_list(void __user *arg)
 {
+       register struct hci_conn *c;
        struct hci_conn_list_req req, *cl;
        struct hci_conn_info *ci;
        struct hci_dev *hdev;
-       struct list_head *p;
        int n = 0, size, err;
 
        if (copy_from_user(&req, arg, sizeof(req)))
@@ -882,10 +883,7 @@ int hci_get_conn_list(void __user *arg)
        ci = cl->conn_info;
 
        hci_dev_lock_bh(hdev);
-       list_for_each(p, &hdev->conn_hash.list) {
-               register struct hci_conn *c;
-               c = list_entry(p, struct hci_conn, list);
-
+       list_for_each_entry(c, &hdev->conn_hash.list, list) {
                bacpy(&(ci + n)->bdaddr, &c->dst);
                (ci + n)->handle = c->handle;
                (ci + n)->type  = c->type;
@@ -956,3 +954,52 @@ int hci_get_auth_info(struct hci_dev *hdev, void __user *arg)
 
        return copy_to_user(arg, &req, sizeof(req)) ? -EFAULT : 0;
 }
+
+struct hci_chan *hci_chan_create(struct hci_conn *conn)
+{
+       struct hci_dev *hdev = conn->hdev;
+       struct hci_chan *chan;
+
+       BT_DBG("%s conn %p", hdev->name, conn);
+
+       chan = kzalloc(sizeof(struct hci_chan), GFP_ATOMIC);
+       if (!chan)
+               return NULL;
+
+       chan->conn = conn;
+       skb_queue_head_init(&chan->data_q);
+
+       tasklet_disable(&hdev->tx_task);
+       hci_chan_hash_add(conn, chan);
+       tasklet_enable(&hdev->tx_task);
+
+       return chan;
+}
+
+int hci_chan_del(struct hci_chan *chan)
+{
+       struct hci_conn *conn = chan->conn;
+       struct hci_dev *hdev = conn->hdev;
+
+       BT_DBG("%s conn %p chan %p", hdev->name, conn, chan);
+
+       tasklet_disable(&hdev->tx_task);
+       hci_chan_hash_del(conn, chan);
+       tasklet_enable(&hdev->tx_task);
+
+       skb_queue_purge(&chan->data_q);
+       kfree(chan);
+
+       return 0;
+}
+
+void hci_chan_hash_flush(struct hci_conn *conn)
+{
+       struct hci_chan_hash *h = &conn->chan_hash;
+       struct hci_chan *chan, *tmp;
+
+       BT_DBG("conn %p", conn);
+
+       list_for_each_entry_safe(chan, tmp, &h->list, list)
+               hci_chan_del(chan);
+}
index be84ae33ae36bec88f4f67502780b4fe5333e270..fb3feeb185d7098359bdd71b0156fa8a67d0157f 100644 (file)
@@ -319,8 +319,7 @@ static void hci_linkpol_req(struct hci_dev *hdev, unsigned long opt)
  * Device is held on return. */
 struct hci_dev *hci_dev_get(int index)
 {
-       struct hci_dev *hdev = NULL;
-       struct list_head *p;
+       struct hci_dev *hdev = NULL, *d;
 
        BT_DBG("%d", index);
 
@@ -328,8 +327,7 @@ struct hci_dev *hci_dev_get(int index)
                return NULL;
 
        read_lock(&hci_dev_list_lock);
-       list_for_each(p, &hci_dev_list) {
-               struct hci_dev *d = list_entry(p, struct hci_dev, list);
+       list_for_each_entry(d, &hci_dev_list, list) {
                if (d->id == index) {
                        hdev = hci_dev_hold(d);
                        break;
@@ -551,8 +549,11 @@ int hci_dev_open(__u16 dev)
                hci_dev_hold(hdev);
                set_bit(HCI_UP, &hdev->flags);
                hci_notify(hdev, HCI_DEV_UP);
-               if (!test_bit(HCI_SETUP, &hdev->flags))
-                       mgmt_powered(hdev->id, 1);
+               if (!test_bit(HCI_SETUP, &hdev->flags)) {
+                       hci_dev_lock_bh(hdev);
+                       mgmt_powered(hdev, 1);
+                       hci_dev_unlock_bh(hdev);
+               }
        } else {
                /* Init failed, cleanup */
                tasklet_kill(&hdev->rx_task);
@@ -597,6 +598,14 @@ static int hci_dev_do_close(struct hci_dev *hdev)
        tasklet_kill(&hdev->rx_task);
        tasklet_kill(&hdev->tx_task);
 
+       if (hdev->discov_timeout > 0) {
+               cancel_delayed_work(&hdev->discov_off);
+               hdev->discov_timeout = 0;
+       }
+
+       if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->flags))
+               cancel_delayed_work(&hdev->power_off);
+
        hci_dev_lock_bh(hdev);
        inquiry_cache_flush(hdev);
        hci_conn_hash_flush(hdev);
@@ -636,7 +645,9 @@ static int hci_dev_do_close(struct hci_dev *hdev)
         * and no tasks are scheduled. */
        hdev->close(hdev);
 
-       mgmt_powered(hdev->id, 0);
+       hci_dev_lock_bh(hdev);
+       mgmt_powered(hdev, 0);
+       hci_dev_unlock_bh(hdev);
 
        /* Clear flags */
        hdev->flags = 0;
@@ -794,9 +805,9 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg)
 
 int hci_get_dev_list(void __user *arg)
 {
+       struct hci_dev *hdev;
        struct hci_dev_list_req *dl;
        struct hci_dev_req *dr;
-       struct list_head *p;
        int n = 0, size, err;
        __u16 dev_num;
 
@@ -815,12 +826,9 @@ int hci_get_dev_list(void __user *arg)
        dr = dl->dev_req;
 
        read_lock_bh(&hci_dev_list_lock);
-       list_for_each(p, &hci_dev_list) {
-               struct hci_dev *hdev;
-
-               hdev = list_entry(p, struct hci_dev, list);
-
-               hci_del_off_timer(hdev);
+       list_for_each_entry(hdev, &hci_dev_list, list) {
+               if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->flags))
+                       cancel_delayed_work(&hdev->power_off);
 
                if (!test_bit(HCI_MGMT, &hdev->flags))
                        set_bit(HCI_PAIRABLE, &hdev->flags);
@@ -855,7 +863,8 @@ int hci_get_dev_info(void __user *arg)
        if (!hdev)
                return -ENODEV;
 
-       hci_del_off_timer(hdev);
+       if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->flags))
+               cancel_delayed_work_sync(&hdev->power_off);
 
        if (!test_bit(HCI_MGMT, &hdev->flags))
                set_bit(HCI_PAIRABLE, &hdev->flags);
@@ -912,6 +921,7 @@ struct hci_dev *hci_alloc_dev(void)
        if (!hdev)
                return NULL;
 
+       hci_init_sysfs(hdev);
        skb_queue_head_init(&hdev->driver_init);
 
        return hdev;
@@ -938,39 +948,41 @@ static void hci_power_on(struct work_struct *work)
                return;
 
        if (test_bit(HCI_AUTO_OFF, &hdev->flags))
-               mod_timer(&hdev->off_timer,
-                               jiffies + msecs_to_jiffies(AUTO_OFF_TIMEOUT));
+               queue_delayed_work(hdev->workqueue, &hdev->power_off,
+                                       msecs_to_jiffies(AUTO_OFF_TIMEOUT));
 
        if (test_and_clear_bit(HCI_SETUP, &hdev->flags))
-               mgmt_index_added(hdev->id);
+               mgmt_index_added(hdev);
 }
 
 static void hci_power_off(struct work_struct *work)
 {
-       struct hci_dev *hdev = container_of(work, struct hci_dev, power_off);
+       struct hci_dev *hdev = container_of(work, struct hci_dev,
+                                                       power_off.work);
 
        BT_DBG("%s", hdev->name);
 
+       clear_bit(HCI_AUTO_OFF, &hdev->flags);
+
        hci_dev_close(hdev->id);
 }
 
-static void hci_auto_off(unsigned long data)
+static void hci_discov_off(struct work_struct *work)
 {
-       struct hci_dev *hdev = (struct hci_dev *) data;
+       struct hci_dev *hdev;
+       u8 scan = SCAN_PAGE;
+
+       hdev = container_of(work, struct hci_dev, discov_off.work);
 
        BT_DBG("%s", hdev->name);
 
-       clear_bit(HCI_AUTO_OFF, &hdev->flags);
+       hci_dev_lock_bh(hdev);
 
-       queue_work(hdev->workqueue, &hdev->power_off);
-}
+       hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, sizeof(scan), &scan);
 
-void hci_del_off_timer(struct hci_dev *hdev)
-{
-       BT_DBG("%s", hdev->name);
+       hdev->discov_timeout = 0;
 
-       clear_bit(HCI_AUTO_OFF, &hdev->flags);
-       del_timer(&hdev->off_timer);
+       hci_dev_unlock_bh(hdev);
 }
 
 int hci_uuids_clear(struct hci_dev *hdev)
@@ -1007,16 +1019,11 @@ int hci_link_keys_clear(struct hci_dev *hdev)
 
 struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr)
 {
-       struct list_head *p;
-
-       list_for_each(p, &hdev->link_keys) {
-               struct link_key *k;
-
-               k = list_entry(p, struct link_key, list);
+       struct link_key *k;
 
+       list_for_each_entry(k, &hdev->link_keys, list)
                if (bacmp(bdaddr, &k->bdaddr) == 0)
                        return k;
-       }
 
        return NULL;
 }
@@ -1138,7 +1145,7 @@ int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key,
 
        persistent = hci_persistent_key(hdev, conn, type, old_key_type);
 
-       mgmt_new_key(hdev->id, key, persistent);
+       mgmt_new_link_key(hdev, key, persistent);
 
        if (!persistent) {
                list_del(&key->list);
@@ -1181,7 +1188,7 @@ int hci_add_ltk(struct hci_dev *hdev, int new_key, bdaddr_t *bdaddr,
        memcpy(id->rand, rand, sizeof(id->rand));
 
        if (new_key)
-               mgmt_new_key(hdev->id, key, old_key_type);
+               mgmt_new_link_key(hdev, key, old_key_type);
 
        return 0;
 }
@@ -1279,16 +1286,11 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash,
 struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev,
                                                bdaddr_t *bdaddr)
 {
-       struct list_head *p;
-
-       list_for_each(p, &hdev->blacklist) {
-               struct bdaddr_list *b;
-
-               b = list_entry(p, struct bdaddr_list, list);
+       struct bdaddr_list *b;
 
+       list_for_each_entry(b, &hdev->blacklist, list)
                if (bacmp(bdaddr, &b->bdaddr) == 0)
                        return b;
-       }
 
        return NULL;
 }
@@ -1327,7 +1329,7 @@ int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr)
 
        list_add(&entry->list, &hdev->blacklist);
 
-       return mgmt_device_blocked(hdev->id, bdaddr);
+       return mgmt_device_blocked(hdev, bdaddr);
 }
 
 int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr)
@@ -1346,7 +1348,7 @@ int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr)
        list_del(&entry->list);
        kfree(entry);
 
-       return mgmt_device_unblocked(hdev->id, bdaddr);
+       return mgmt_device_unblocked(hdev, bdaddr);
 }
 
 static void hci_clear_adv_cache(unsigned long arg)
@@ -1425,7 +1427,7 @@ int hci_add_adv_entry(struct hci_dev *hdev,
 int hci_register_dev(struct hci_dev *hdev)
 {
        struct list_head *head = &hci_dev_list, *p;
-       int i, id = 0;
+       int i, id, error;
 
        BT_DBG("%p name %s bus %d owner %p", hdev, hdev->name,
                                                hdev->bus, hdev->owner);
@@ -1433,6 +1435,11 @@ int hci_register_dev(struct hci_dev *hdev)
        if (!hdev->open || !hdev->close || !hdev->destruct)
                return -EINVAL;
 
+       /* Do not allow HCI_AMP devices to register at index 0,
+        * so the index can be used as the AMP controller ID.
+        */
+       id = (hdev->dev_type == HCI_BREDR) ? 0 : 1;
+
        write_lock_bh(&hci_dev_list_lock);
 
        /* Find first available device id */
@@ -1479,6 +1486,8 @@ int hci_register_dev(struct hci_dev *hdev)
 
        hci_conn_hash_init(hdev);
 
+       INIT_LIST_HEAD(&hdev->mgmt_pending);
+
        INIT_LIST_HEAD(&hdev->blacklist);
 
        INIT_LIST_HEAD(&hdev->uuids);
@@ -1492,8 +1501,9 @@ int hci_register_dev(struct hci_dev *hdev)
                                                (unsigned long) hdev);
 
        INIT_WORK(&hdev->power_on, hci_power_on);
-       INIT_WORK(&hdev->power_off, hci_power_off);
-       setup_timer(&hdev->off_timer, hci_auto_off, (unsigned long) hdev);
+       INIT_DELAYED_WORK(&hdev->power_off, hci_power_off);
+
+       INIT_DELAYED_WORK(&hdev->discov_off, hci_discov_off);
 
        memset(&hdev->stat, 0, sizeof(struct hci_dev_stats));
 
@@ -1502,10 +1512,14 @@ int hci_register_dev(struct hci_dev *hdev)
        write_unlock_bh(&hci_dev_list_lock);
 
        hdev->workqueue = create_singlethread_workqueue(hdev->name);
-       if (!hdev->workqueue)
-               goto nomem;
+       if (!hdev->workqueue) {
+               error = -ENOMEM;
+               goto err;
+       }
 
-       hci_register_sysfs(hdev);
+       error = hci_add_sysfs(hdev);
+       if (error < 0)
+               goto err_wqueue;
 
        hdev->rfkill = rfkill_alloc(hdev->name, &hdev->dev,
                                RFKILL_TYPE_BLUETOOTH, &hci_rfkill_ops, hdev);
@@ -1524,17 +1538,19 @@ int hci_register_dev(struct hci_dev *hdev)
 
        return id;
 
-nomem:
+err_wqueue:
+       destroy_workqueue(hdev->workqueue);
+err:
        write_lock_bh(&hci_dev_list_lock);
        list_del(&hdev->list);
        write_unlock_bh(&hci_dev_list_lock);
 
-       return -ENOMEM;
+       return error;
 }
 EXPORT_SYMBOL(hci_register_dev);
 
 /* Unregister HCI device */
-int hci_unregister_dev(struct hci_dev *hdev)
+void hci_unregister_dev(struct hci_dev *hdev)
 {
        int i;
 
@@ -1550,8 +1566,15 @@ int hci_unregister_dev(struct hci_dev *hdev)
                kfree_skb(hdev->reassembly[i]);
 
        if (!test_bit(HCI_INIT, &hdev->flags) &&
-                                       !test_bit(HCI_SETUP, &hdev->flags))
-               mgmt_index_removed(hdev->id);
+                                       !test_bit(HCI_SETUP, &hdev->flags)) {
+               hci_dev_lock_bh(hdev);
+               mgmt_index_removed(hdev);
+               hci_dev_unlock_bh(hdev);
+       }
+
+       /* mgmt_index_removed should take care of emptying the
+        * pending list */
+       BUG_ON(!list_empty(&hdev->mgmt_pending));
 
        hci_notify(hdev, HCI_DEV_UNREG);
 
@@ -1560,9 +1583,8 @@ int hci_unregister_dev(struct hci_dev *hdev)
                rfkill_destroy(hdev->rfkill);
        }
 
-       hci_unregister_sysfs(hdev);
+       hci_del_sysfs(hdev);
 
-       hci_del_off_timer(hdev);
        del_timer(&hdev->adv_timer);
 
        destroy_workqueue(hdev->workqueue);
@@ -1576,8 +1598,6 @@ int hci_unregister_dev(struct hci_dev *hdev)
        hci_dev_unlock_bh(hdev);
 
        __hci_dev_put(hdev);
-
-       return 0;
 }
 EXPORT_SYMBOL(hci_unregister_dev);
 
@@ -1948,23 +1968,18 @@ static void hci_add_acl_hdr(struct sk_buff *skb, __u16 handle, __u16 flags)
        hdr->dlen   = cpu_to_le16(len);
 }
 
-void hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags)
+static void hci_queue_acl(struct hci_conn *conn, struct sk_buff_head *queue,
+                               struct sk_buff *skb, __u16 flags)
 {
        struct hci_dev *hdev = conn->hdev;
        struct sk_buff *list;
 
-       BT_DBG("%s conn %p flags 0x%x", hdev->name, conn, flags);
-
-       skb->dev = (void *) hdev;
-       bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT;
-       hci_add_acl_hdr(skb, conn->handle, flags);
-
        list = skb_shinfo(skb)->frag_list;
        if (!list) {
                /* Non fragmented */
                BT_DBG("%s nonfrag skb %p len %d", hdev->name, skb, skb->len);
 
-               skb_queue_tail(&conn->data_q, skb);
+               skb_queue_tail(queue, skb);
        } else {
                /* Fragmented */
                BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len);
@@ -1972,9 +1987,9 @@ void hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags)
                skb_shinfo(skb)->frag_list = NULL;
 
                /* Queue all fragments atomically */
-               spin_lock_bh(&conn->data_q.lock);
+               spin_lock_bh(&queue->lock);
 
-               __skb_queue_tail(&conn->data_q, skb);
+               __skb_queue_tail(queue, skb);
 
                flags &= ~ACL_START;
                flags |= ACL_CONT;
@@ -1987,11 +2002,25 @@ void hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags)
 
                        BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len);
 
-                       __skb_queue_tail(&conn->data_q, skb);
+                       __skb_queue_tail(queue, skb);
                } while (list);
 
-               spin_unlock_bh(&conn->data_q.lock);
+               spin_unlock_bh(&queue->lock);
        }
+}
+
+void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags)
+{
+       struct hci_conn *conn = chan->conn;
+       struct hci_dev *hdev = conn->hdev;
+
+       BT_DBG("%s chan %p flags 0x%x", hdev->name, chan, flags);
+
+       skb->dev = (void *) hdev;
+       bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT;
+       hci_add_acl_hdr(skb, conn->handle, flags);
+
+       hci_queue_acl(conn, &chan->data_q, skb, flags);
 
        tasklet_schedule(&hdev->tx_task);
 }
@@ -2026,16 +2055,12 @@ EXPORT_SYMBOL(hci_send_sco);
 static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int *quote)
 {
        struct hci_conn_hash *h = &hdev->conn_hash;
-       struct hci_conn *conn = NULL;
+       struct hci_conn *conn = NULL, *c;
        int num = 0, min = ~0;
-       struct list_head *p;
 
        /* We don't have to lock device here. Connections are always
         * added and removed with TX task disabled. */
-       list_for_each(p, &h->list) {
-               struct hci_conn *c;
-               c = list_entry(p, struct hci_conn, list);
-
+       list_for_each_entry(c, &h->list, list) {
                if (c->type != type || skb_queue_empty(&c->data_q))
                        continue;
 
@@ -2084,14 +2109,12 @@ static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int
 static inline void hci_link_tx_to(struct hci_dev *hdev, __u8 type)
 {
        struct hci_conn_hash *h = &hdev->conn_hash;
-       struct list_head *p;
-       struct hci_conn  *c;
+       struct hci_conn *c;
 
        BT_ERR("%s link tx timeout", hdev->name);
 
        /* Kill stalled connections */
-       list_for_each(p, &h->list) {
-               c = list_entry(p, struct hci_conn, list);
+       list_for_each_entry(c, &h->list, list) {
                if (c->type == type && c->sent) {
                        BT_ERR("%s killing stalled connection %s",
                                hdev->name, batostr(&c->dst));
@@ -2100,11 +2123,137 @@ static inline void hci_link_tx_to(struct hci_dev *hdev, __u8 type)
        }
 }
 
-static inline void hci_sched_acl(struct hci_dev *hdev)
+static inline struct hci_chan *hci_chan_sent(struct hci_dev *hdev, __u8 type,
+                                               int *quote)
 {
+       struct hci_conn_hash *h = &hdev->conn_hash;
+       struct hci_chan *chan = NULL;
+       int num = 0, min = ~0, cur_prio = 0;
        struct hci_conn *conn;
+       int cnt, q, conn_num = 0;
+
+       BT_DBG("%s", hdev->name);
+
+       list_for_each_entry(conn, &h->list, list) {
+               struct hci_chan_hash *ch;
+               struct hci_chan *tmp;
+
+               if (conn->type != type)
+                       continue;
+
+               if (conn->state != BT_CONNECTED && conn->state != BT_CONFIG)
+                       continue;
+
+               conn_num++;
+
+               ch = &conn->chan_hash;
+
+               list_for_each_entry(tmp, &ch->list, list) {
+                       struct sk_buff *skb;
+
+                       if (skb_queue_empty(&tmp->data_q))
+                               continue;
+
+                       skb = skb_peek(&tmp->data_q);
+                       if (skb->priority < cur_prio)
+                               continue;
+
+                       if (skb->priority > cur_prio) {
+                               num = 0;
+                               min = ~0;
+                               cur_prio = skb->priority;
+                       }
+
+                       num++;
+
+                       if (conn->sent < min) {
+                               min  = conn->sent;
+                               chan = tmp;
+                       }
+               }
+
+               if (hci_conn_num(hdev, type) == conn_num)
+                       break;
+       }
+
+       if (!chan)
+               return NULL;
+
+       switch (chan->conn->type) {
+       case ACL_LINK:
+               cnt = hdev->acl_cnt;
+               break;
+       case SCO_LINK:
+       case ESCO_LINK:
+               cnt = hdev->sco_cnt;
+               break;
+       case LE_LINK:
+               cnt = hdev->le_mtu ? hdev->le_cnt : hdev->acl_cnt;
+               break;
+       default:
+               cnt = 0;
+               BT_ERR("Unknown link type");
+       }
+
+       q = cnt / num;
+       *quote = q ? q : 1;
+       BT_DBG("chan %p quote %d", chan, *quote);
+       return chan;
+}
+
+static void hci_prio_recalculate(struct hci_dev *hdev, __u8 type)
+{
+       struct hci_conn_hash *h = &hdev->conn_hash;
+       struct hci_conn *conn;
+       int num = 0;
+
+       BT_DBG("%s", hdev->name);
+
+       list_for_each_entry(conn, &h->list, list) {
+               struct hci_chan_hash *ch;
+               struct hci_chan *chan;
+
+               if (conn->type != type)
+                       continue;
+
+               if (conn->state != BT_CONNECTED && conn->state != BT_CONFIG)
+                       continue;
+
+               num++;
+
+               ch = &conn->chan_hash;
+               list_for_each_entry(chan, &ch->list, list) {
+                       struct sk_buff *skb;
+
+                       if (chan->sent) {
+                               chan->sent = 0;
+                               continue;
+                       }
+
+                       if (skb_queue_empty(&chan->data_q))
+                               continue;
+
+                       skb = skb_peek(&chan->data_q);
+                       if (skb->priority >= HCI_PRIO_MAX - 1)
+                               continue;
+
+                       skb->priority = HCI_PRIO_MAX - 1;
+
+                       BT_DBG("chan %p skb %p promoted to %d", chan, skb,
+                                                               skb->priority);
+               }
+
+               if (hci_conn_num(hdev, type) == num)
+                       break;
+       }
+}
+
+static inline void hci_sched_acl(struct hci_dev *hdev)
+{
+       struct hci_chan *chan;
        struct sk_buff *skb;
        int quote;
+       unsigned int cnt;
 
        BT_DBG("%s", hdev->name);
 
@@ -2118,19 +2267,35 @@ static inline void hci_sched_acl(struct hci_dev *hdev)
                        hci_link_tx_to(hdev, ACL_LINK);
        }
 
-       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);
+       cnt = hdev->acl_cnt;
+
+       while (hdev->acl_cnt &&
+                       (chan = hci_chan_sent(hdev, ACL_LINK, &quote))) {
+               u32 priority = (skb_peek(&chan->data_q))->priority;
+               while (quote-- && (skb = skb_peek(&chan->data_q))) {
+                       BT_DBG("chan %p skb %p len %d priority %u", chan, skb,
+                                       skb->len, skb->priority);
 
-                       hci_conn_enter_active_mode(conn, bt_cb(skb)->force_active);
+                       /* Stop if priority has changed */
+                       if (skb->priority < priority)
+                               break;
+
+                       skb = skb_dequeue(&chan->data_q);
+
+                       hci_conn_enter_active_mode(chan->conn,
+                                               bt_cb(skb)->force_active);
 
                        hci_send_frame(skb);
                        hdev->acl_last_tx = jiffies;
 
                        hdev->acl_cnt--;
-                       conn->sent++;
+                       chan->sent++;
+                       chan->conn->sent++;
                }
        }
+
+       if (cnt != hdev->acl_cnt)
+               hci_prio_recalculate(hdev, ACL_LINK);
 }
 
 /* Schedule SCO */
@@ -2182,9 +2347,9 @@ static inline void hci_sched_esco(struct hci_dev *hdev)
 
 static inline void hci_sched_le(struct hci_dev *hdev)
 {
-       struct hci_conn *conn;
+       struct hci_chan *chan;
        struct sk_buff *skb;
-       int quote, cnt;
+       int quote, cnt, tmp;
 
        BT_DBG("%s", hdev->name);
 
@@ -2200,21 +2365,35 @@ static inline void hci_sched_le(struct hci_dev *hdev)
        }
 
        cnt = hdev->le_pkts ? hdev->le_cnt : hdev->acl_cnt;
-       while (cnt && (conn = hci_low_sent(hdev, LE_LINK, &quote))) {
-               while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
-                       BT_DBG("skb %p len %d", skb, skb->len);
+       tmp = cnt;
+       while (cnt && (chan = hci_chan_sent(hdev, LE_LINK, &quote))) {
+               u32 priority = (skb_peek(&chan->data_q))->priority;
+               while (quote-- && (skb = skb_peek(&chan->data_q))) {
+                       BT_DBG("chan %p skb %p len %d priority %u", chan, skb,
+                                       skb->len, skb->priority);
+
+                       /* Stop if priority has changed */
+                       if (skb->priority < priority)
+                               break;
+
+                       skb = skb_dequeue(&chan->data_q);
 
                        hci_send_frame(skb);
                        hdev->le_last_tx = jiffies;
 
                        cnt--;
-                       conn->sent++;
+                       chan->sent++;
+                       chan->conn->sent++;
                }
        }
+
        if (hdev->le_pkts)
                hdev->le_cnt = cnt;
        else
                hdev->acl_cnt = cnt;
+
+       if (cnt != tmp)
+               hci_prio_recalculate(hdev, LE_LINK);
 }
 
 static void hci_tx_task(unsigned long arg)
@@ -2407,3 +2586,31 @@ static void hci_cmd_task(unsigned long arg)
                }
        }
 }
+
+int hci_do_inquiry(struct hci_dev *hdev, u8 length)
+{
+       /* General inquiry access code (GIAC) */
+       u8 lap[3] = { 0x33, 0x8b, 0x9e };
+       struct hci_cp_inquiry cp;
+
+       BT_DBG("%s", hdev->name);
+
+       if (test_bit(HCI_INQUIRY, &hdev->flags))
+               return -EINPROGRESS;
+
+       memset(&cp, 0, sizeof(cp));
+       memcpy(&cp.lap, lap, sizeof(cp.lap));
+       cp.length  = length;
+
+       return hci_send_cmd(hdev, HCI_OP_INQUIRY, sizeof(cp), &cp);
+}
+
+int hci_cancel_inquiry(struct hci_dev *hdev)
+{
+       BT_DBG("%s", hdev->name);
+
+       if (!test_bit(HCI_INQUIRY, &hdev->flags))
+               return -EPERM;
+
+       return hci_send_cmd(hdev, HCI_OP_INQUIRY_CANCEL, 0, NULL);
+}
index d7d96b6b1f0d63b338e5ed58fb125fbc6290aff5..a89cf1f24e47697d14f009900ac8a3ee8fe0e549 100644 (file)
@@ -58,9 +58,11 @@ static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb)
        if (status)
                return;
 
-       if (test_and_clear_bit(HCI_INQUIRY, &hdev->flags) &&
-                       test_bit(HCI_MGMT, &hdev->flags))
-               mgmt_discovering(hdev->id, 0);
+       clear_bit(HCI_INQUIRY, &hdev->flags);
+
+       hci_dev_lock(hdev);
+       mgmt_discovering(hdev, 0);
+       hci_dev_unlock(hdev);
 
        hci_req_complete(hdev, HCI_OP_INQUIRY_CANCEL, status);
 
@@ -76,10 +78,6 @@ static void hci_cc_exit_periodic_inq(struct hci_dev *hdev, struct sk_buff *skb)
        if (status)
                return;
 
-       if (test_and_clear_bit(HCI_INQUIRY, &hdev->flags) &&
-                               test_bit(HCI_MGMT, &hdev->flags))
-               mgmt_discovering(hdev->id, 0);
-
        hci_conn_check_pending(hdev);
 }
 
@@ -205,13 +203,15 @@ static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb)
        if (!sent)
                return;
 
+       hci_dev_lock(hdev);
+
        if (test_bit(HCI_MGMT, &hdev->flags))
-               mgmt_set_local_name_complete(hdev->id, sent, status);
+               mgmt_set_local_name_complete(hdev, sent, status);
 
-       if (status)
-               return;
+       if (status == 0)
+               memcpy(hdev->dev_name, sent, HCI_MAX_NAME_LENGTH);
 
-       memcpy(hdev->dev_name, sent, HCI_MAX_NAME_LENGTH);
+       hci_dev_unlock(hdev);
 }
 
 static void hci_cc_read_local_name(struct hci_dev *hdev, struct sk_buff *skb)
@@ -274,7 +274,8 @@ static void hci_cc_write_encrypt_mode(struct hci_dev *hdev, struct sk_buff *skb)
 
 static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb)
 {
-       __u8 status = *((__u8 *) skb->data);
+       __u8 param, status = *((__u8 *) skb->data);
+       int old_pscan, old_iscan;
        void *sent;
 
        BT_DBG("%s status 0x%x", hdev->name, status);
@@ -283,28 +284,40 @@ static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb)
        if (!sent)
                return;
 
-       if (!status) {
-               __u8 param = *((__u8 *) sent);
-               int old_pscan, old_iscan;
-
-               old_pscan = test_and_clear_bit(HCI_PSCAN, &hdev->flags);
-               old_iscan = test_and_clear_bit(HCI_ISCAN, &hdev->flags);
+       param = *((__u8 *) sent);
 
-               if (param & SCAN_INQUIRY) {
-                       set_bit(HCI_ISCAN, &hdev->flags);
-                       if (!old_iscan)
-                               mgmt_discoverable(hdev->id, 1);
-               } else if (old_iscan)
-                       mgmt_discoverable(hdev->id, 0);
+       hci_dev_lock(hdev);
 
-               if (param & SCAN_PAGE) {
-                       set_bit(HCI_PSCAN, &hdev->flags);
-                       if (!old_pscan)
-                               mgmt_connectable(hdev->id, 1);
-               } else if (old_pscan)
-                       mgmt_connectable(hdev->id, 0);
+       if (status != 0) {
+               mgmt_write_scan_failed(hdev, param, status);
+               hdev->discov_timeout = 0;
+               goto done;
        }
 
+       old_pscan = test_and_clear_bit(HCI_PSCAN, &hdev->flags);
+       old_iscan = test_and_clear_bit(HCI_ISCAN, &hdev->flags);
+
+       if (param & SCAN_INQUIRY) {
+               set_bit(HCI_ISCAN, &hdev->flags);
+               if (!old_iscan)
+                       mgmt_discoverable(hdev, 1);
+               if (hdev->discov_timeout > 0) {
+                       int to = msecs_to_jiffies(hdev->discov_timeout * 1000);
+                       queue_delayed_work(hdev->workqueue, &hdev->discov_off,
+                                                                       to);
+               }
+       } else if (old_iscan)
+               mgmt_discoverable(hdev, 0);
+
+       if (param & SCAN_PAGE) {
+               set_bit(HCI_PSCAN, &hdev->flags);
+               if (!old_pscan)
+                       mgmt_connectable(hdev, 1);
+       } else if (old_pscan)
+               mgmt_connectable(hdev, 0);
+
+done:
+       hci_dev_unlock(hdev);
        hci_req_complete(hdev, HCI_OP_WRITE_SCAN_ENABLE, status);
 }
 
@@ -748,6 +761,30 @@ static void hci_cc_write_ca_timeout(struct hci_dev *hdev, struct sk_buff *skb)
        hci_req_complete(hdev, HCI_OP_WRITE_CA_TIMEOUT, status);
 }
 
+static void hci_cc_read_local_amp_info(struct hci_dev *hdev,
+               struct sk_buff *skb)
+{
+       struct hci_rp_read_local_amp_info *rp = (void *) skb->data;
+
+       BT_DBG("%s status 0x%x", hdev->name, rp->status);
+
+       if (rp->status)
+               return;
+
+       hdev->amp_status = rp->amp_status;
+       hdev->amp_total_bw = __le32_to_cpu(rp->total_bw);
+       hdev->amp_max_bw = __le32_to_cpu(rp->max_bw);
+       hdev->amp_min_latency = __le32_to_cpu(rp->min_latency);
+       hdev->amp_max_pdu = __le32_to_cpu(rp->max_pdu);
+       hdev->amp_type = rp->amp_type;
+       hdev->amp_pal_cap = __le16_to_cpu(rp->pal_cap);
+       hdev->amp_assoc_size = __le16_to_cpu(rp->max_assoc_size);
+       hdev->amp_be_flush_to = __le32_to_cpu(rp->be_flush_to);
+       hdev->amp_max_flush_to = __le32_to_cpu(rp->max_flush_to);
+
+       hci_req_complete(hdev, HCI_OP_READ_LOCAL_AMP_INFO, rp->status);
+}
+
 static void hci_cc_delete_stored_link_key(struct hci_dev *hdev,
                                                        struct sk_buff *skb)
 {
@@ -804,19 +841,24 @@ static void hci_cc_pin_code_reply(struct hci_dev *hdev, struct sk_buff *skb)
 
        BT_DBG("%s status 0x%x", hdev->name, rp->status);
 
+       hci_dev_lock(hdev);
+
        if (test_bit(HCI_MGMT, &hdev->flags))
-               mgmt_pin_code_reply_complete(hdev->id, &rp->bdaddr, rp->status);
+               mgmt_pin_code_reply_complete(hdev, &rp->bdaddr, rp->status);
 
        if (rp->status != 0)
-               return;
+               goto unlock;
 
        cp = hci_sent_cmd_data(hdev, HCI_OP_PIN_CODE_REPLY);
        if (!cp)
-               return;
+               goto unlock;
 
        conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr);
        if (conn)
                conn->pin_length = cp->pin_len;
+
+unlock:
+       hci_dev_unlock(hdev);
 }
 
 static void hci_cc_pin_code_neg_reply(struct hci_dev *hdev, struct sk_buff *skb)
@@ -825,10 +867,15 @@ static void hci_cc_pin_code_neg_reply(struct hci_dev *hdev, struct sk_buff *skb)
 
        BT_DBG("%s status 0x%x", hdev->name, rp->status);
 
+       hci_dev_lock(hdev);
+
        if (test_bit(HCI_MGMT, &hdev->flags))
-               mgmt_pin_code_neg_reply_complete(hdev->id, &rp->bdaddr,
+               mgmt_pin_code_neg_reply_complete(hdev, &rp->bdaddr,
                                                                rp->status);
+
+       hci_dev_unlock(hdev);
 }
+
 static void hci_cc_le_read_buffer_size(struct hci_dev *hdev,
                                       struct sk_buff *skb)
 {
@@ -855,9 +902,13 @@ static void hci_cc_user_confirm_reply(struct hci_dev *hdev, struct sk_buff *skb)
 
        BT_DBG("%s status 0x%x", hdev->name, rp->status);
 
+       hci_dev_lock(hdev);
+
        if (test_bit(HCI_MGMT, &hdev->flags))
-               mgmt_user_confirm_reply_complete(hdev->id, &rp->bdaddr,
+               mgmt_user_confirm_reply_complete(hdev, &rp->bdaddr,
                                                                rp->status);
+
+       hci_dev_unlock(hdev);
 }
 
 static void hci_cc_user_confirm_neg_reply(struct hci_dev *hdev,
@@ -867,9 +918,13 @@ static void hci_cc_user_confirm_neg_reply(struct hci_dev *hdev,
 
        BT_DBG("%s status 0x%x", hdev->name, rp->status);
 
+       hci_dev_lock(hdev);
+
        if (test_bit(HCI_MGMT, &hdev->flags))
-               mgmt_user_confirm_neg_reply_complete(hdev->id, &rp->bdaddr,
+               mgmt_user_confirm_neg_reply_complete(hdev, &rp->bdaddr,
                                                                rp->status);
+
+       hci_dev_unlock(hdev);
 }
 
 static void hci_cc_read_local_oob_data_reply(struct hci_dev *hdev,
@@ -879,8 +934,10 @@ static void hci_cc_read_local_oob_data_reply(struct hci_dev *hdev,
 
        BT_DBG("%s status 0x%x", hdev->name, rp->status);
 
-       mgmt_read_local_oob_data_reply_complete(hdev->id, rp->hash,
+       hci_dev_lock(hdev);
+       mgmt_read_local_oob_data_reply_complete(hdev, rp->hash,
                                                rp->randomizer, rp->status);
+       hci_dev_unlock(hdev);
 }
 
 static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
@@ -955,12 +1012,18 @@ static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
        if (status) {
                hci_req_complete(hdev, HCI_OP_INQUIRY, status);
                hci_conn_check_pending(hdev);
+               hci_dev_lock(hdev);
+               if (test_bit(HCI_MGMT, &hdev->flags))
+                       mgmt_inquiry_failed(hdev, status);
+               hci_dev_unlock(hdev);
                return;
        }
 
-       if (!test_and_set_bit(HCI_INQUIRY, &hdev->flags) &&
-                               test_bit(HCI_MGMT, &hdev->flags))
-               mgmt_discovering(hdev->id, 1);
+       set_bit(HCI_INQUIRY, &hdev->flags);
+
+       hci_dev_lock(hdev);
+       mgmt_discovering(hdev, 1);
+       hci_dev_unlock(hdev);
 }
 
 static inline void hci_cs_create_conn(struct hci_dev *hdev, __u8 status)
@@ -1339,13 +1402,16 @@ static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff
 
        BT_DBG("%s status %d", hdev->name, status);
 
-       if (test_and_clear_bit(HCI_INQUIRY, &hdev->flags) &&
-                               test_bit(HCI_MGMT, &hdev->flags))
-               mgmt_discovering(hdev->id, 0);
-
        hci_req_complete(hdev, HCI_OP_INQUIRY, status);
 
        hci_conn_check_pending(hdev);
+
+       if (!test_and_clear_bit(HCI_INQUIRY, &hdev->flags))
+               return;
+
+       hci_dev_lock(hdev);
+       mgmt_discovering(hdev, 0);
+       hci_dev_unlock(hdev);
 }
 
 static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb)
@@ -1361,12 +1427,6 @@ static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *
 
        hci_dev_lock(hdev);
 
-       if (!test_and_set_bit(HCI_INQUIRY, &hdev->flags)) {
-
-               if (test_bit(HCI_MGMT, &hdev->flags))
-                       mgmt_discovering(hdev->id, 1);
-       }
-
        for (; num_rsp; num_rsp--, info++) {
                bacpy(&data.bdaddr, &info->bdaddr);
                data.pscan_rep_mode     = info->pscan_rep_mode;
@@ -1377,8 +1437,8 @@ static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *
                data.rssi               = 0x00;
                data.ssp_mode           = 0x00;
                hci_inquiry_cache_update(hdev, &data);
-               mgmt_device_found(hdev->id, &info->bdaddr, info->dev_class, 0,
-                                                                       NULL);
+               mgmt_device_found(hdev, &info->bdaddr, ACL_LINK,
+                                               info->dev_class, 0, NULL);
        }
 
        hci_dev_unlock(hdev);
@@ -1412,7 +1472,7 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
                        conn->state = BT_CONFIG;
                        hci_conn_hold(conn);
                        conn->disc_timeout = HCI_DISCONN_TIMEOUT;
-                       mgmt_connected(hdev->id, &ev->bdaddr, conn->type);
+                       mgmt_connected(hdev, &ev->bdaddr, conn->type);
                } else
                        conn->state = BT_CONNECTED;
 
@@ -1444,7 +1504,8 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
        } else {
                conn->state = BT_CLOSED;
                if (conn->type == ACL_LINK)
-                       mgmt_connect_failed(hdev->id, &ev->bdaddr, ev->status);
+                       mgmt_connect_failed(hdev, &ev->bdaddr, conn->type,
+                                                               ev->status);
        }
 
        if (conn->type == ACL_LINK)
@@ -1531,7 +1592,7 @@ static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *sk
                struct hci_cp_reject_conn_req cp;
 
                bacpy(&cp.bdaddr, &ev->bdaddr);
-               cp.reason = 0x0f;
+               cp.reason = HCI_ERROR_REJ_BAD_ADDR;
                hci_send_cmd(hdev, HCI_OP_REJECT_CONN_REQ, sizeof(cp), &cp);
        }
 }
@@ -1544,7 +1605,9 @@ static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff
        BT_DBG("%s status %d", hdev->name, ev->status);
 
        if (ev->status) {
-               mgmt_disconnect_failed(hdev->id);
+               hci_dev_lock(hdev);
+               mgmt_disconnect_failed(hdev);
+               hci_dev_unlock(hdev);
                return;
        }
 
@@ -1557,7 +1620,7 @@ static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff
        conn->state = BT_CLOSED;
 
        if (conn->type == ACL_LINK || conn->type == LE_LINK)
-               mgmt_disconnected(hdev->id, &conn->dst);
+               mgmt_disconnected(hdev, &conn->dst, conn->type);
 
        hci_proto_disconn_cfm(conn, ev->reason);
        hci_conn_del(conn);
@@ -1588,7 +1651,7 @@ static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *s
                        conn->sec_level = conn->pending_sec_level;
                }
        } else {
-               mgmt_auth_failed(hdev->id, &conn->dst, ev->status);
+               mgmt_auth_failed(hdev, &conn->dst, ev->status);
        }
 
        clear_bit(HCI_CONN_AUTH_PEND, &conn->pend);
@@ -1643,7 +1706,7 @@ static inline void hci_remote_name_evt(struct hci_dev *hdev, struct sk_buff *skb
        hci_dev_lock(hdev);
 
        if (ev->status == 0 && test_bit(HCI_MGMT, &hdev->flags))
-               mgmt_remote_name(hdev->id, &ev->bdaddr, ev->name);
+               mgmt_remote_name(hdev, &ev->bdaddr, ev->name);
 
        conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
        if (!conn)
@@ -1898,6 +1961,10 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk
                hci_cc_write_ca_timeout(hdev, skb);
                break;
 
+       case HCI_OP_READ_LOCAL_AMP_INFO:
+               hci_cc_read_local_amp_info(hdev, skb);
+               break;
+
        case HCI_OP_DELETE_STORED_LINK_KEY:
                hci_cc_delete_stored_link_key(hdev, skb);
                break;
@@ -2029,7 +2096,7 @@ static inline void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
 
        case HCI_OP_DISCONNECT:
                if (ev->status != 0)
-                       mgmt_disconnect_failed(hdev->id);
+                       mgmt_disconnect_failed(hdev);
                break;
 
        case HCI_OP_LE_CREATE_CONN:
@@ -2194,7 +2261,7 @@ static inline void hci_pin_code_request_evt(struct hci_dev *hdev, struct sk_buff
                else
                        secure = 0;
 
-               mgmt_pin_code_request(hdev->id, &ev->bdaddr, secure);
+               mgmt_pin_code_request(hdev, &ev->bdaddr, secure);
        }
 
 unlock:
@@ -2363,12 +2430,6 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct
 
        hci_dev_lock(hdev);
 
-       if (!test_and_set_bit(HCI_INQUIRY, &hdev->flags)) {
-
-               if (test_bit(HCI_MGMT, &hdev->flags))
-                       mgmt_discovering(hdev->id, 1);
-       }
-
        if ((skb->len - 1) / num_rsp != sizeof(struct inquiry_info_with_rssi)) {
                struct inquiry_info_with_rssi_and_pscan_mode *info;
                info = (void *) (skb->data + 1);
@@ -2383,7 +2444,7 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct
                        data.rssi               = info->rssi;
                        data.ssp_mode           = 0x00;
                        hci_inquiry_cache_update(hdev, &data);
-                       mgmt_device_found(hdev->id, &info->bdaddr,
+                       mgmt_device_found(hdev, &info->bdaddr, ACL_LINK,
                                                info->dev_class, info->rssi,
                                                NULL);
                }
@@ -2400,7 +2461,7 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct
                        data.rssi               = info->rssi;
                        data.ssp_mode           = 0x00;
                        hci_inquiry_cache_update(hdev, &data);
-                       mgmt_device_found(hdev->id, &info->bdaddr,
+                       mgmt_device_found(hdev, &info->bdaddr, ACL_LINK,
                                                info->dev_class, info->rssi,
                                                NULL);
                }
@@ -2531,12 +2592,6 @@ static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct
        if (!num_rsp)
                return;
 
-       if (!test_and_set_bit(HCI_INQUIRY, &hdev->flags)) {
-
-               if (test_bit(HCI_MGMT, &hdev->flags))
-                       mgmt_discovering(hdev->id, 1);
-       }
-
        hci_dev_lock(hdev);
 
        for (; num_rsp; num_rsp--, info++) {
@@ -2549,8 +2604,8 @@ static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct
                data.rssi               = info->rssi;
                data.ssp_mode           = 0x01;
                hci_inquiry_cache_update(hdev, &data);
-               mgmt_device_found(hdev->id, &info->bdaddr, info->dev_class,
-                                               info->rssi, info->data);
+               mgmt_device_found(hdev, &info->bdaddr, ACL_LINK,
+                               info->dev_class, info->rssi, info->data);
        }
 
        hci_dev_unlock(hdev);
@@ -2614,7 +2669,7 @@ static inline void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff
                struct hci_cp_io_capability_neg_reply cp;
 
                bacpy(&cp.bdaddr, &ev->bdaddr);
-               cp.reason = 0x18; /* Pairing not allowed */
+               cp.reason = HCI_ERROR_PAIRING_NOT_ALLOWED;
 
                hci_send_cmd(hdev, HCI_OP_IO_CAPABILITY_NEG_REPLY,
                                                        sizeof(cp), &cp);
@@ -2706,7 +2761,7 @@ static inline void hci_user_confirm_request_evt(struct hci_dev *hdev,
        }
 
 confirm:
-       mgmt_user_confirm_request(hdev->id, &ev->bdaddr, ev->passkey,
+       mgmt_user_confirm_request(hdev, &ev->bdaddr, ev->passkey,
                                                                confirm_hint);
 
 unlock:
@@ -2732,7 +2787,7 @@ static inline void hci_simple_pair_complete_evt(struct hci_dev *hdev, struct sk_
         * event gets always produced as initiator and is also mapped to
         * the mgmt_auth_failed event */
        if (!test_bit(HCI_CONN_AUTH_PEND, &conn->pend) && ev->status != 0)
-               mgmt_auth_failed(hdev->id, &conn->dst, ev->status);
+               mgmt_auth_failed(hdev, &conn->dst, ev->status);
 
        hci_conn_put(conn);
 
@@ -2813,14 +2868,14 @@ static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff
        }
 
        if (ev->status) {
-               mgmt_connect_failed(hdev->id, &ev->bdaddr, ev->status);
+               mgmt_connect_failed(hdev, &ev->bdaddr, conn->type, ev->status);
                hci_proto_connect_cfm(conn, ev->status);
                conn->state = BT_CLOSED;
                hci_conn_del(conn);
                goto unlock;
        }
 
-       mgmt_connected(hdev->id, &ev->bdaddr, conn->type);
+       mgmt_connected(hdev, &ev->bdaddr, conn->type);
 
        conn->sec_level = BT_SECURITY_LOW;
        conn->handle = __le16_to_cpu(ev->handle);
@@ -3104,5 +3159,5 @@ void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data)
        kfree_skb(skb);
 }
 
-module_param(enable_le, bool, 0444);
+module_param(enable_le, bool, 0644);
 MODULE_PARM_DESC(enable_le, "Enable LE support");
index 22f1a6c87035b551c352acf70d53ed369d6e9dcd..f8e6aa386cef5fb9efed93292826ac9919667be2 100644 (file)
@@ -435,17 +435,12 @@ static const struct file_operations inquiry_cache_fops = {
 static int blacklist_show(struct seq_file *f, void *p)
 {
        struct hci_dev *hdev = f->private;
-       struct list_head *l;
+       struct bdaddr_list *b;
 
        hci_dev_lock_bh(hdev);
 
-       list_for_each(l, &hdev->blacklist) {
-               struct bdaddr_list *b;
-
-               b = list_entry(l, struct bdaddr_list, list);
-
+       list_for_each_entry(b, &hdev->blacklist, list)
                seq_printf(f, "%s\n", batostr(&b->bdaddr));
-       }
 
        hci_dev_unlock_bh(hdev);
 
@@ -484,17 +479,12 @@ static void print_bt_uuid(struct seq_file *f, u8 *uuid)
 static int uuids_show(struct seq_file *f, void *p)
 {
        struct hci_dev *hdev = f->private;
-       struct list_head *l;
+       struct bt_uuid *uuid;
 
        hci_dev_lock_bh(hdev);
 
-       list_for_each(l, &hdev->uuids) {
-               struct bt_uuid *uuid;
-
-               uuid = list_entry(l, struct bt_uuid, list);
-
+       list_for_each_entry(uuid, &hdev->uuids, list)
                print_bt_uuid(f, uuid->uuid);
-       }
 
        hci_dev_unlock_bh(hdev);
 
@@ -542,22 +532,28 @@ static int auto_accept_delay_get(void *data, u64 *val)
 DEFINE_SIMPLE_ATTRIBUTE(auto_accept_delay_fops, auto_accept_delay_get,
                                        auto_accept_delay_set, "%llu\n");
 
-int hci_register_sysfs(struct hci_dev *hdev)
+void hci_init_sysfs(struct hci_dev *hdev)
+{
+       struct device *dev = &hdev->dev;
+
+       dev->type = &bt_host;
+       dev->class = bt_class;
+
+       dev_set_drvdata(dev, hdev);
+       device_initialize(dev);
+}
+
+int hci_add_sysfs(struct hci_dev *hdev)
 {
        struct device *dev = &hdev->dev;
        int err;
 
        BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus);
 
-       dev->type = &bt_host;
-       dev->class = bt_class;
        dev->parent = hdev->parent;
-
        dev_set_name(dev, "%s", hdev->name);
 
-       dev_set_drvdata(dev, hdev);
-
-       err = device_register(dev);
+       err = device_add(dev);
        if (err < 0)
                return err;
 
@@ -581,7 +577,7 @@ int hci_register_sysfs(struct hci_dev *hdev)
        return 0;
 }
 
-void hci_unregister_sysfs(struct hci_dev *hdev)
+void hci_del_sysfs(struct hci_dev *hdev)
 {
        BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus);
 
index 075a3e920caffcbd44ff2f5a1e9e6ab4cc560e45..3c2d888925d7a8436b19ef8d1562fe9a7943a780 100644 (file)
@@ -81,24 +81,20 @@ static unsigned char hidp_mkeyspat[] = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 };
 static struct hidp_session *__hidp_get_session(bdaddr_t *bdaddr)
 {
        struct hidp_session *session;
-       struct list_head *p;
 
        BT_DBG("");
 
-       list_for_each(p, &hidp_session_list) {
-               session = list_entry(p, struct hidp_session, list);
+       list_for_each_entry(session, &hidp_session_list, list) {
                if (!bacmp(bdaddr, &session->bdaddr))
                        return session;
        }
+
        return NULL;
 }
 
 static void __hidp_link_session(struct hidp_session *session)
 {
-       __module_get(THIS_MODULE);
        list_add(&session->list, &hidp_session_list);
-
-       hci_conn_hold_device(session->conn);
 }
 
 static void __hidp_unlink_session(struct hidp_session *session)
@@ -106,7 +102,6 @@ static void __hidp_unlink_session(struct hidp_session *session)
        hci_conn_put_device(session->conn);
 
        list_del(&session->list);
-       module_put(THIS_MODULE);
 }
 
 static void __hidp_copy_session(struct hidp_session *session, struct hidp_conninfo *ci)
@@ -255,6 +250,9 @@ static int __hidp_send_ctrl_message(struct hidp_session *session,
 
        BT_DBG("session %p data %p size %d", session, data, size);
 
+       if (atomic_read(&session->terminate))
+               return -EIO;
+
        skb = alloc_skb(size + 1, GFP_ATOMIC);
        if (!skb) {
                BT_ERR("Can't allocate memory for new frame");
@@ -329,6 +327,7 @@ static int hidp_get_raw_report(struct hid_device *hid,
        struct sk_buff *skb;
        size_t len;
        int numbered_reports = hid->report_enum[report_type].numbered;
+       int ret;
 
        switch (report_type) {
        case HID_FEATURE_REPORT:
@@ -352,8 +351,9 @@ static int hidp_get_raw_report(struct hid_device *hid,
        session->waiting_report_number = numbered_reports ? report_number : -1;
        set_bit(HIDP_WAITING_FOR_RETURN, &session->flags);
        data[0] = report_number;
-       if (hidp_send_ctrl_message(hid->driver_data, report_type, data, 1))
-               goto err_eio;
+       ret = hidp_send_ctrl_message(hid->driver_data, report_type, data, 1);
+       if (ret)
+               goto err;
 
        /* Wait for the return of the report. The returned report
           gets put in session->report_return.  */
@@ -365,11 +365,13 @@ static int hidp_get_raw_report(struct hid_device *hid,
                        5*HZ);
                if (res == 0) {
                        /* timeout */
-                       goto err_eio;
+                       ret = -EIO;
+                       goto err;
                }
                if (res < 0) {
                        /* signal */
-                       goto err_restartsys;
+                       ret = -ERESTARTSYS;
+                       goto err;
                }
        }
 
@@ -390,14 +392,10 @@ static int hidp_get_raw_report(struct hid_device *hid,
 
        return len;
 
-err_restartsys:
-       clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags);
-       mutex_unlock(&session->report_mutex);
-       return -ERESTARTSYS;
-err_eio:
+err:
        clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags);
        mutex_unlock(&session->report_mutex);
-       return -EIO;
+       return ret;
 }
 
 static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, size_t count,
@@ -422,11 +420,10 @@ static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, s
 
        /* Set up our wait, and send the report request to the device. */
        set_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags);
-       if (hidp_send_ctrl_message(hid->driver_data, report_type,
-                       data, count)) {
-               ret = -ENOMEM;
+       ret = hidp_send_ctrl_message(hid->driver_data, report_type, data,
+                                                                       count);
+       if (ret)
                goto err;
-       }
 
        /* Wait for the ACK from the device. */
        while (test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags)) {
@@ -496,10 +493,9 @@ static void hidp_process_handshake(struct hidp_session *session,
        case HIDP_HSHK_ERR_INVALID_REPORT_ID:
        case HIDP_HSHK_ERR_UNSUPPORTED_REQUEST:
        case HIDP_HSHK_ERR_INVALID_PARAMETER:
-               if (test_bit(HIDP_WAITING_FOR_RETURN, &session->flags)) {
-                       clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags);
+               if (test_and_clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags))
                        wake_up_interruptible(&session->report_queue);
-               }
+
                /* FIXME: Call into SET_ GET_ handlers here */
                break;
 
@@ -520,10 +516,8 @@ static void hidp_process_handshake(struct hidp_session *session,
        }
 
        /* Wake up the waiting thread. */
-       if (test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags)) {
-               clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags);
+       if (test_and_clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags))
                wake_up_interruptible(&session->report_queue);
-       }
 }
 
 static void hidp_process_hid_control(struct hidp_session *session,
@@ -663,25 +657,32 @@ static int hidp_send_frame(struct socket *sock, unsigned char *data, int len)
        return kernel_sendmsg(sock, &msg, &iv, 1, len);
 }
 
-static void hidp_process_transmit(struct hidp_session *session)
+static void hidp_process_intr_transmit(struct hidp_session *session)
 {
        struct sk_buff *skb;
 
        BT_DBG("session %p", session);
 
-       while ((skb = skb_dequeue(&session->ctrl_transmit))) {
-               if (hidp_send_frame(session->ctrl_sock, skb->data, skb->len) < 0) {
-                       skb_queue_head(&session->ctrl_transmit, skb);
+       while ((skb = skb_dequeue(&session->intr_transmit))) {
+               if (hidp_send_frame(session->intr_sock, skb->data, skb->len) < 0) {
+                       skb_queue_head(&session->intr_transmit, skb);
                        break;
                }
 
                hidp_set_timer(session);
                kfree_skb(skb);
        }
+}
 
-       while ((skb = skb_dequeue(&session->intr_transmit))) {
-               if (hidp_send_frame(session->intr_sock, skb->data, skb->len) < 0) {
-                       skb_queue_head(&session->intr_transmit, skb);
+static void hidp_process_ctrl_transmit(struct hidp_session *session)
+{
+       struct sk_buff *skb;
+
+       BT_DBG("session %p", session);
+
+       while ((skb = skb_dequeue(&session->ctrl_transmit))) {
+               if (hidp_send_frame(session->ctrl_sock, skb->data, skb->len) < 0) {
+                       skb_queue_head(&session->ctrl_transmit, skb);
                        break;
                }
 
@@ -700,6 +701,7 @@ static int hidp_session(void *arg)
 
        BT_DBG("session %p", session);
 
+       __module_get(THIS_MODULE);
        set_user_nice(current, -15);
 
        init_waitqueue_entry(&ctrl_wait, current);
@@ -714,23 +716,25 @@ static int hidp_session(void *arg)
                                intr_sk->sk_state != BT_CONNECTED)
                        break;
 
-               while ((skb = skb_dequeue(&ctrl_sk->sk_receive_queue))) {
+               while ((skb = skb_dequeue(&intr_sk->sk_receive_queue))) {
                        skb_orphan(skb);
                        if (!skb_linearize(skb))
-                               hidp_recv_ctrl_frame(session, skb);
+                               hidp_recv_intr_frame(session, skb);
                        else
                                kfree_skb(skb);
                }
 
-               while ((skb = skb_dequeue(&intr_sk->sk_receive_queue))) {
+               hidp_process_intr_transmit(session);
+
+               while ((skb = skb_dequeue(&ctrl_sk->sk_receive_queue))) {
                        skb_orphan(skb);
                        if (!skb_linearize(skb))
-                               hidp_recv_intr_frame(session, skb);
+                               hidp_recv_ctrl_frame(session, skb);
                        else
                                kfree_skb(skb);
                }
 
-               hidp_process_transmit(session);
+               hidp_process_ctrl_transmit(session);
 
                schedule();
                set_current_state(TASK_INTERRUPTIBLE);
@@ -739,6 +743,10 @@ static int hidp_session(void *arg)
        remove_wait_queue(sk_sleep(intr_sk), &intr_wait);
        remove_wait_queue(sk_sleep(ctrl_sk), &ctrl_wait);
 
+       clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags);
+       clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags);
+       wake_up_interruptible(&session->report_queue);
+
        down_write(&hidp_session_sem);
 
        hidp_del_timer(session);
@@ -772,34 +780,37 @@ static int hidp_session(void *arg)
 
        kfree(session->rd_data);
        kfree(session);
+       module_put_and_exit(0);
        return 0;
 }
 
-static struct device *hidp_get_device(struct hidp_session *session)
+static struct hci_conn *hidp_get_connection(struct hidp_session *session)
 {
        bdaddr_t *src = &bt_sk(session->ctrl_sock->sk)->src;
        bdaddr_t *dst = &bt_sk(session->ctrl_sock->sk)->dst;
-       struct device *device = NULL;
+       struct hci_conn *conn;
        struct hci_dev *hdev;
 
        hdev = hci_get_route(dst, src);
        if (!hdev)
                return NULL;
 
-       session->conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
-       if (session->conn)
-               device = &session->conn->dev;
+       hci_dev_lock_bh(hdev);
+       conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
+       if (conn)
+               hci_conn_hold_device(conn);
+       hci_dev_unlock_bh(hdev);
 
        hci_dev_put(hdev);
 
-       return device;
+       return conn;
 }
 
 static int hidp_setup_input(struct hidp_session *session,
                                struct hidp_connadd_req *req)
 {
        struct input_dev *input;
-       int err, i;
+       int i;
 
        input = input_allocate_device();
        if (!input)
@@ -842,17 +853,10 @@ static int hidp_setup_input(struct hidp_session *session,
                input->relbit[0] |= BIT_MASK(REL_WHEEL);
        }
 
-       input->dev.parent = hidp_get_device(session);
+       input->dev.parent = &session->conn->dev;
 
        input->event = hidp_input_event;
 
-       err = input_register_device(input);
-       if (err < 0) {
-               input_free_device(input);
-               session->input = NULL;
-               return err;
-       }
-
        return 0;
 }
 
@@ -949,7 +953,7 @@ static int hidp_setup_hid(struct hidp_session *session,
        strncpy(hid->phys, batostr(&bt_sk(session->ctrl_sock->sk)->src), 64);
        strncpy(hid->uniq, batostr(&bt_sk(session->ctrl_sock->sk)->dst), 64);
 
-       hid->dev.parent = hidp_get_device(session);
+       hid->dev.parent = &session->conn->dev;
        hid->ll_driver = &hidp_hid_driver;
 
        hid->hid_get_raw_report = hidp_get_raw_report;
@@ -976,18 +980,20 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
                        bacmp(&bt_sk(ctrl_sock->sk)->dst, &bt_sk(intr_sock->sk)->dst))
                return -ENOTUNIQ;
 
-       session = kzalloc(sizeof(struct hidp_session), GFP_KERNEL);
-       if (!session)
-               return -ENOMEM;
-
        BT_DBG("rd_data %p rd_size %d", req->rd_data, req->rd_size);
 
        down_write(&hidp_session_sem);
 
        s = __hidp_get_session(&bt_sk(ctrl_sock->sk)->dst);
        if (s && s->state == BT_CONNECTED) {
-               err = -EEXIST;
-               goto failed;
+               up_write(&hidp_session_sem);
+               return -EEXIST;
+       }
+
+       session = kzalloc(sizeof(struct hidp_session), GFP_KERNEL);
+       if (!session) {
+               up_write(&hidp_session_sem);
+               return -ENOMEM;
        }
 
        bacpy(&session->bdaddr, &bt_sk(ctrl_sock->sk)->dst);
@@ -1003,6 +1009,12 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
        session->intr_sock = intr_sock;
        session->state     = BT_CONNECTED;
 
+       session->conn = hidp_get_connection(session);
+       if (!session->conn) {
+               err = -ENOTCONN;
+               goto failed;
+       }
+
        setup_timer(&session->timer, hidp_idle_timeout, (unsigned long)session);
 
        skb_queue_head_init(&session->ctrl_transmit);
@@ -1015,9 +1027,11 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
        session->flags   = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID);
        session->idle_to = req->idle_to;
 
+       __hidp_link_session(session);
+
        if (req->rd_size > 0) {
                err = hidp_setup_hid(session, req);
-               if (err && err != -ENODEV)
+               if (err)
                        goto purge;
        }
 
@@ -1027,8 +1041,6 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
                        goto purge;
        }
 
-       __hidp_link_session(session);
-
        hidp_set_timer(session);
 
        if (session->hid) {
@@ -1054,7 +1066,11 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
                        !session->waiting_for_startup);
        }
 
-       err = hid_add_device(session->hid);
+       if (session->hid)
+               err = hid_add_device(session->hid);
+       else
+               err = input_register_device(session->input);
+
        if (err < 0) {
                atomic_inc(&session->terminate);
                wake_up_process(session->task);
@@ -1077,8 +1093,6 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
 unlink:
        hidp_del_timer(session);
 
-       __hidp_unlink_session(session);
-
        if (session->input) {
                input_unregister_device(session->input);
                session->input = NULL;
@@ -1093,6 +1107,8 @@ unlink:
        session->rd_data = NULL;
 
 purge:
+       __hidp_unlink_session(session);
+
        skb_queue_purge(&session->ctrl_transmit);
        skb_queue_purge(&session->intr_transmit);
 
@@ -1134,19 +1150,16 @@ int hidp_del_connection(struct hidp_conndel_req *req)
 
 int hidp_get_connlist(struct hidp_connlist_req *req)
 {
-       struct list_head *p;
+       struct hidp_session *session;
        int err = 0, n = 0;
 
        BT_DBG("");
 
        down_read(&hidp_session_sem);
 
-       list_for_each(p, &hidp_session_list) {
-               struct hidp_session *session;
+       list_for_each_entry(session, &hidp_session_list, list) {
                struct hidp_conninfo ci;
 
-               session = list_entry(p, struct hidp_session, list);
-
                __hidp_copy_session(session, &ci);
 
                if (copy_to_user(req->ci, &ci, sizeof(ci))) {
index 5ea94a1eecf2f9a4338aa116d645b03b77fa7bd6..e8a6837996cf311c3156c6c7b18ffc92cb0fba84 100644 (file)
 #include <net/bluetooth/smp.h>
 
 int disable_ertm;
+int enable_hs;
 
 static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN;
-static u8 l2cap_fixed_chan[8] = { 0x02, };
+static u8 l2cap_fixed_chan[8] = { L2CAP_FC_L2CAP, };
 
 static LIST_HEAD(chan_list);
 static DEFINE_RWLOCK(chan_list_lock);
@@ -219,7 +220,7 @@ static u16 l2cap_alloc_cid(struct l2cap_conn *conn)
 
 static void l2cap_set_timer(struct l2cap_chan *chan, struct timer_list *timer, long timeout)
 {
-       BT_DBG("chan %p state %d timeout %ld", chan->sk, chan->state, timeout);
+       BT_DBG("chan %p state %d timeout %ld", chan, chan->state, timeout);
 
        if (!mod_timer(timer, jiffies + msecs_to_jiffies(timeout)))
                chan_hold(chan);
@@ -293,6 +294,8 @@ struct l2cap_chan *l2cap_chan_create(struct sock *sk)
 
        atomic_set(&chan->refcnt, 1);
 
+       BT_DBG("sk %p chan %p", sk, chan);
+
        return chan;
 }
 
@@ -310,7 +313,7 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
        BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn,
                        chan->psm, chan->dcid);
 
-       conn->disc_reason = 0x13;
+       conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM;
 
        chan->conn = conn;
 
@@ -337,6 +340,13 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
                chan->omtu = L2CAP_DEFAULT_MTU;
        }
 
+       chan->local_id          = L2CAP_BESTEFFORT_ID;
+       chan->local_stype       = L2CAP_SERV_BESTEFFORT;
+       chan->local_msdu        = L2CAP_DEFAULT_MAX_SDU_SIZE;
+       chan->local_sdu_itime   = L2CAP_DEFAULT_SDU_ITIME;
+       chan->local_acc_lat     = L2CAP_DEFAULT_ACC_LAT;
+       chan->local_flush_to    = L2CAP_DEFAULT_FLUSH_TO;
+
        chan_hold(chan);
 
        list_add(&chan->list, &conn->chan_l);
@@ -556,34 +566,58 @@ static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len,
                flags = ACL_START;
 
        bt_cb(skb)->force_active = BT_POWER_FORCE_ACTIVE_ON;
+       skb->priority = HCI_PRIO_MAX;
+
+       hci_send_acl(conn->hchan, skb, flags);
+}
+
+static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb)
+{
+       struct hci_conn *hcon = chan->conn->hcon;
+       u16 flags;
+
+       BT_DBG("chan %p, skb %p len %d priority %u", chan, skb, skb->len,
+                                                       skb->priority);
+
+       if (!test_bit(FLAG_FLUSHABLE, &chan->flags) &&
+                                       lmp_no_flush_capable(hcon->hdev))
+               flags = ACL_START_NO_FLUSH;
+       else
+               flags = ACL_START;
 
-       hci_send_acl(conn->hcon, skb, flags);
+       bt_cb(skb)->force_active = test_bit(FLAG_FORCE_ACTIVE, &chan->flags);
+       hci_send_acl(chan->conn->hchan, skb, flags);
 }
 
-static inline void l2cap_send_sframe(struct l2cap_chan *chan, u16 control)
+static inline void l2cap_send_sframe(struct l2cap_chan *chan, u32 control)
 {
        struct sk_buff *skb;
        struct l2cap_hdr *lh;
        struct l2cap_conn *conn = chan->conn;
-       int count, hlen = L2CAP_HDR_SIZE + 2;
-       u8 flags;
+       int count, hlen;
 
        if (chan->state != BT_CONNECTED)
                return;
 
+       if (test_bit(FLAG_EXT_CTRL, &chan->flags))
+               hlen = L2CAP_EXT_HDR_SIZE;
+       else
+               hlen = L2CAP_ENH_HDR_SIZE;
+
        if (chan->fcs == L2CAP_FCS_CRC16)
-               hlen += 2;
+               hlen += L2CAP_FCS_SIZE;
 
-       BT_DBG("chan %p, control 0x%2.2x", chan, control);
+       BT_DBG("chan %p, control 0x%8.8x", chan, control);
 
        count = min_t(unsigned int, conn->mtu, hlen);
-       control |= L2CAP_CTRL_FRAME_TYPE;
+
+       control |= __set_sframe(chan);
 
        if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state))
-               control |= L2CAP_CTRL_FINAL;
+               control |= __set_ctrl_final(chan);
 
        if (test_and_clear_bit(CONN_SEND_PBIT, &chan->conn_state))
-               control |= L2CAP_CTRL_POLL;
+               control |= __set_ctrl_poll(chan);
 
        skb = bt_skb_alloc(count, GFP_ATOMIC);
        if (!skb)
@@ -592,32 +626,27 @@ static inline void l2cap_send_sframe(struct l2cap_chan *chan, u16 control)
        lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
        lh->len = cpu_to_le16(hlen - L2CAP_HDR_SIZE);
        lh->cid = cpu_to_le16(chan->dcid);
-       put_unaligned_le16(control, skb_put(skb, 2));
+
+       __put_control(chan, control, skb_put(skb, __ctrl_size(chan)));
 
        if (chan->fcs == L2CAP_FCS_CRC16) {
-               u16 fcs = crc16(0, (u8 *)lh, count - 2);
-               put_unaligned_le16(fcs, skb_put(skb, 2));
+               u16 fcs = crc16(0, (u8 *)lh, count - L2CAP_FCS_SIZE);
+               put_unaligned_le16(fcs, skb_put(skb, L2CAP_FCS_SIZE));
        }
 
-       if (lmp_no_flush_capable(conn->hcon->hdev))
-               flags = ACL_START_NO_FLUSH;
-       else
-               flags = ACL_START;
-
-       bt_cb(skb)->force_active = chan->force_active;
-
-       hci_send_acl(chan->conn->hcon, skb, flags);
+       skb->priority = HCI_PRIO_MAX;
+       l2cap_do_send(chan, skb);
 }
 
-static inline void l2cap_send_rr_or_rnr(struct l2cap_chan *chan, u16 control)
+static inline void l2cap_send_rr_or_rnr(struct l2cap_chan *chan, u32 control)
 {
        if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
-               control |= L2CAP_SUPER_RCV_NOT_READY;
+               control |= __set_ctrl_super(chan, L2CAP_SUPER_RNR);
                set_bit(CONN_RNR_SENT, &chan->conn_state);
        } else
-               control |= L2CAP_SUPER_RCV_READY;
+               control |= __set_ctrl_super(chan, L2CAP_SUPER_RR);
 
-       control |= chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT;
+       control |= __set_reqseq(chan, chan->buffer_seq);
 
        l2cap_send_sframe(chan, control);
 }
@@ -947,7 +976,7 @@ static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err)
        list_for_each_entry(chan, &conn->chan_l, list) {
                struct sock *sk = chan->sk;
 
-               if (chan->force_reliable)
+               if (test_bit(FLAG_FORCE_RELIABLE, &chan->flags))
                        sk->sk_err = err;
        }
 
@@ -986,6 +1015,8 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
                chan->ops->close(chan->data);
        }
 
+       hci_chan_del(conn->hchan);
+
        if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT)
                del_timer_sync(&conn->info_timer);
 
@@ -1008,18 +1039,26 @@ static void security_timeout(unsigned long arg)
 static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
 {
        struct l2cap_conn *conn = hcon->l2cap_data;
+       struct hci_chan *hchan;
 
        if (conn || status)
                return conn;
 
+       hchan = hci_chan_create(hcon);
+       if (!hchan)
+               return NULL;
+
        conn = kzalloc(sizeof(struct l2cap_conn), GFP_ATOMIC);
-       if (!conn)
+       if (!conn) {
+               hci_chan_del(hchan);
                return NULL;
+       }
 
        hcon->l2cap_data = conn;
        conn->hcon = hcon;
+       conn->hchan = hchan;
 
-       BT_DBG("hcon %p conn %p", hcon, conn);
+       BT_DBG("hcon %p conn %p hchan %p", hcon, conn, hchan);
 
        if (hcon->hdev->le_mtu && hcon->type == LE_LINK)
                conn->mtu = hcon->hdev->le_mtu;
@@ -1043,7 +1082,7 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
                setup_timer(&conn->info_timer, l2cap_info_timeout,
                                                (unsigned long) conn);
 
-       conn->disc_reason = 0x13;
+       conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM;
 
        return conn;
 }
@@ -1245,47 +1284,35 @@ static void l2cap_drop_acked_frames(struct l2cap_chan *chan)
                __clear_retrans_timer(chan);
 }
 
-static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb)
-{
-       struct hci_conn *hcon = chan->conn->hcon;
-       u16 flags;
-
-       BT_DBG("chan %p, skb %p len %d", chan, skb, skb->len);
-
-       if (!chan->flushable && lmp_no_flush_capable(hcon->hdev))
-               flags = ACL_START_NO_FLUSH;
-       else
-               flags = ACL_START;
-
-       bt_cb(skb)->force_active = chan->force_active;
-       hci_send_acl(hcon, skb, flags);
-}
-
 static void l2cap_streaming_send(struct l2cap_chan *chan)
 {
        struct sk_buff *skb;
-       u16 control, fcs;
+       u32 control;
+       u16 fcs;
 
        while ((skb = skb_dequeue(&chan->tx_q))) {
-               control = get_unaligned_le16(skb->data + L2CAP_HDR_SIZE);
-               control |= chan->next_tx_seq << L2CAP_CTRL_TXSEQ_SHIFT;
-               put_unaligned_le16(control, skb->data + L2CAP_HDR_SIZE);
+               control = __get_control(chan, skb->data + L2CAP_HDR_SIZE);
+               control |= __set_txseq(chan, chan->next_tx_seq);
+               __put_control(chan, control, skb->data + L2CAP_HDR_SIZE);
 
                if (chan->fcs == L2CAP_FCS_CRC16) {
-                       fcs = crc16(0, (u8 *)skb->data, skb->len - 2);
-                       put_unaligned_le16(fcs, skb->data + skb->len - 2);
+                       fcs = crc16(0, (u8 *)skb->data,
+                                               skb->len - L2CAP_FCS_SIZE);
+                       put_unaligned_le16(fcs,
+                                       skb->data + skb->len - L2CAP_FCS_SIZE);
                }
 
                l2cap_do_send(chan, skb);
 
-               chan->next_tx_seq = (chan->next_tx_seq + 1) % 64;
+               chan->next_tx_seq = __next_seq(chan, chan->next_tx_seq);
        }
 }
 
-static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u8 tx_seq)
+static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u16 tx_seq)
 {
        struct sk_buff *skb, *tx_skb;
-       u16 control, fcs;
+       u16 fcs;
+       u32 control;
 
        skb = skb_peek(&chan->tx_q);
        if (!skb)
@@ -1308,20 +1335,23 @@ static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u8 tx_seq)
 
        tx_skb = skb_clone(skb, GFP_ATOMIC);
        bt_cb(skb)->retries++;
-       control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE);
-       control &= L2CAP_CTRL_SAR;
+
+       control = __get_control(chan, tx_skb->data + L2CAP_HDR_SIZE);
+       control &= __get_sar_mask(chan);
 
        if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state))
-               control |= L2CAP_CTRL_FINAL;
+               control |= __set_ctrl_final(chan);
 
-       control |= (chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT)
-                       | (tx_seq << L2CAP_CTRL_TXSEQ_SHIFT);
+       control |= __set_reqseq(chan, chan->buffer_seq);
+       control |= __set_txseq(chan, tx_seq);
 
-       put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE);
+       __put_control(chan, control, tx_skb->data + L2CAP_HDR_SIZE);
 
        if (chan->fcs == L2CAP_FCS_CRC16) {
-               fcs = crc16(0, (u8 *)tx_skb->data, tx_skb->len - 2);
-               put_unaligned_le16(fcs, tx_skb->data + tx_skb->len - 2);
+               fcs = crc16(0, (u8 *)tx_skb->data,
+                                               tx_skb->len - L2CAP_FCS_SIZE);
+               put_unaligned_le16(fcs,
+                               tx_skb->data + tx_skb->len - L2CAP_FCS_SIZE);
        }
 
        l2cap_do_send(chan, tx_skb);
@@ -1330,7 +1360,8 @@ static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u8 tx_seq)
 static int l2cap_ertm_send(struct l2cap_chan *chan)
 {
        struct sk_buff *skb, *tx_skb;
-       u16 control, fcs;
+       u16 fcs;
+       u32 control;
        int nsent = 0;
 
        if (chan->state != BT_CONNECTED)
@@ -1348,20 +1379,22 @@ static int l2cap_ertm_send(struct l2cap_chan *chan)
 
                bt_cb(skb)->retries++;
 
-               control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE);
-               control &= L2CAP_CTRL_SAR;
+               control = __get_control(chan, tx_skb->data + L2CAP_HDR_SIZE);
+               control &= __get_sar_mask(chan);
 
                if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state))
-                       control |= L2CAP_CTRL_FINAL;
+                       control |= __set_ctrl_final(chan);
 
-               control |= (chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT)
-                               | (chan->next_tx_seq << L2CAP_CTRL_TXSEQ_SHIFT);
-               put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE);
+               control |= __set_reqseq(chan, chan->buffer_seq);
+               control |= __set_txseq(chan, chan->next_tx_seq);
 
+               __put_control(chan, control, tx_skb->data + L2CAP_HDR_SIZE);
 
                if (chan->fcs == L2CAP_FCS_CRC16) {
-                       fcs = crc16(0, (u8 *)skb->data, tx_skb->len - 2);
-                       put_unaligned_le16(fcs, skb->data + tx_skb->len - 2);
+                       fcs = crc16(0, (u8 *)skb->data,
+                                               tx_skb->len - L2CAP_FCS_SIZE);
+                       put_unaligned_le16(fcs, skb->data +
+                                               tx_skb->len - L2CAP_FCS_SIZE);
                }
 
                l2cap_do_send(chan, tx_skb);
@@ -1369,7 +1402,8 @@ static int l2cap_ertm_send(struct l2cap_chan *chan)
                __set_retrans_timer(chan);
 
                bt_cb(skb)->tx_seq = chan->next_tx_seq;
-               chan->next_tx_seq = (chan->next_tx_seq + 1) % 64;
+
+               chan->next_tx_seq = __next_seq(chan, chan->next_tx_seq);
 
                if (bt_cb(skb)->retries == 1)
                        chan->unacked_frames++;
@@ -1401,12 +1435,12 @@ static int l2cap_retransmit_frames(struct l2cap_chan *chan)
 
 static void l2cap_send_ack(struct l2cap_chan *chan)
 {
-       u16 control = 0;
+       u32 control = 0;
 
-       control |= chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT;
+       control |= __set_reqseq(chan, chan->buffer_seq);
 
        if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
-               control |= L2CAP_SUPER_RCV_NOT_READY;
+               control |= __set_ctrl_super(chan, L2CAP_SUPER_RNR);
                set_bit(CONN_RNR_SENT, &chan->conn_state);
                l2cap_send_sframe(chan, control);
                return;
@@ -1415,20 +1449,20 @@ static void l2cap_send_ack(struct l2cap_chan *chan)
        if (l2cap_ertm_send(chan) > 0)
                return;
 
-       control |= L2CAP_SUPER_RCV_READY;
+       control |= __set_ctrl_super(chan, L2CAP_SUPER_RR);
        l2cap_send_sframe(chan, control);
 }
 
 static void l2cap_send_srejtail(struct l2cap_chan *chan)
 {
        struct srej_list *tail;
-       u16 control;
+       u32 control;
 
-       control = L2CAP_SUPER_SELECT_REJECT;
-       control |= L2CAP_CTRL_FINAL;
+       control = __set_ctrl_super(chan, L2CAP_SUPER_SREJ);
+       control |= __set_ctrl_final(chan);
 
        tail = list_entry((&chan->srej_l)->prev, struct srej_list, list);
-       control |= tail->tx_seq << L2CAP_CTRL_REQSEQ_SHIFT;
+       control |= __set_reqseq(chan, tail->tx_seq);
 
        l2cap_send_sframe(chan, control);
 }
@@ -1456,6 +1490,8 @@ static inline int l2cap_skbuff_fromiovec(struct sock *sk, struct msghdr *msg, in
                if (memcpy_fromiovec(skb_put(*frag, count), msg->msg_iov, count))
                        return -EFAULT;
 
+               (*frag)->priority = skb->priority;
+
                sent += count;
                len  -= count;
 
@@ -1465,15 +1501,17 @@ static inline int l2cap_skbuff_fromiovec(struct sock *sk, struct msghdr *msg, in
        return sent;
 }
 
-static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len)
+static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan,
+                                               struct msghdr *msg, size_t len,
+                                               u32 priority)
 {
        struct sock *sk = chan->sk;
        struct l2cap_conn *conn = chan->conn;
        struct sk_buff *skb;
-       int err, count, hlen = L2CAP_HDR_SIZE + 2;
+       int err, count, hlen = L2CAP_HDR_SIZE + L2CAP_PSMLEN_SIZE;
        struct l2cap_hdr *lh;
 
-       BT_DBG("sk %p len %d", sk, (int)len);
+       BT_DBG("sk %p len %d priority %u", sk, (int)len, priority);
 
        count = min_t(unsigned int, (conn->mtu - hlen), len);
        skb = bt_skb_send_alloc(sk, count + hlen,
@@ -1481,6 +1519,8 @@ static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, struct
        if (!skb)
                return ERR_PTR(err);
 
+       skb->priority = priority;
+
        /* Create L2CAP header */
        lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
        lh->cid = cpu_to_le16(chan->dcid);
@@ -1495,7 +1535,9 @@ static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, struct
        return skb;
 }
 
-static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len)
+static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan,
+                                               struct msghdr *msg, size_t len,
+                                               u32 priority)
 {
        struct sock *sk = chan->sk;
        struct l2cap_conn *conn = chan->conn;
@@ -1511,6 +1553,8 @@ static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan, struct ms
        if (!skb)
                return ERR_PTR(err);
 
+       skb->priority = priority;
+
        /* Create L2CAP header */
        lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
        lh->cid = cpu_to_le16(chan->dcid);
@@ -1526,12 +1570,12 @@ static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan, struct ms
 
 static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan,
                                                struct msghdr *msg, size_t len,
-                                               u16 control, u16 sdulen)
+                                               u32 control, u16 sdulen)
 {
        struct sock *sk = chan->sk;
        struct l2cap_conn *conn = chan->conn;
        struct sk_buff *skb;
-       int err, count, hlen = L2CAP_HDR_SIZE + 2;
+       int err, count, hlen;
        struct l2cap_hdr *lh;
 
        BT_DBG("sk %p len %d", sk, (int)len);
@@ -1539,11 +1583,16 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan,
        if (!conn)
                return ERR_PTR(-ENOTCONN);
 
+       if (test_bit(FLAG_EXT_CTRL, &chan->flags))
+               hlen = L2CAP_EXT_HDR_SIZE;
+       else
+               hlen = L2CAP_ENH_HDR_SIZE;
+
        if (sdulen)
-               hlen += 2;
+               hlen += L2CAP_SDULEN_SIZE;
 
        if (chan->fcs == L2CAP_FCS_CRC16)
-               hlen += 2;
+               hlen += L2CAP_FCS_SIZE;
 
        count = min_t(unsigned int, (conn->mtu - hlen), len);
        skb = bt_skb_send_alloc(sk, count + hlen,
@@ -1555,9 +1604,11 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan,
        lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
        lh->cid = cpu_to_le16(chan->dcid);
        lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE));
-       put_unaligned_le16(control, skb_put(skb, 2));
+
+       __put_control(chan, control, skb_put(skb, __ctrl_size(chan)));
+
        if (sdulen)
-               put_unaligned_le16(sdulen, skb_put(skb, 2));
+               put_unaligned_le16(sdulen, skb_put(skb, L2CAP_SDULEN_SIZE));
 
        err = l2cap_skbuff_fromiovec(sk, msg, len, count, skb);
        if (unlikely(err < 0)) {
@@ -1566,7 +1617,7 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan,
        }
 
        if (chan->fcs == L2CAP_FCS_CRC16)
-               put_unaligned_le16(0, skb_put(skb, 2));
+               put_unaligned_le16(0, skb_put(skb, L2CAP_FCS_SIZE));
 
        bt_cb(skb)->retries = 0;
        return skb;
@@ -1576,11 +1627,11 @@ static int l2cap_sar_segment_sdu(struct l2cap_chan *chan, struct msghdr *msg, si
 {
        struct sk_buff *skb;
        struct sk_buff_head sar_queue;
-       u16 control;
+       u32 control;
        size_t size = 0;
 
        skb_queue_head_init(&sar_queue);
-       control = L2CAP_SDU_START;
+       control = __set_ctrl_sar(chan, L2CAP_SAR_START);
        skb = l2cap_create_iframe_pdu(chan, msg, chan->remote_mps, control, len);
        if (IS_ERR(skb))
                return PTR_ERR(skb);
@@ -1593,10 +1644,10 @@ static int l2cap_sar_segment_sdu(struct l2cap_chan *chan, struct msghdr *msg, si
                size_t buflen;
 
                if (len > chan->remote_mps) {
-                       control = L2CAP_SDU_CONTINUE;
+                       control = __set_ctrl_sar(chan, L2CAP_SAR_CONTINUE);
                        buflen = chan->remote_mps;
                } else {
-                       control = L2CAP_SDU_END;
+                       control = __set_ctrl_sar(chan, L2CAP_SAR_END);
                        buflen = len;
                }
 
@@ -1617,15 +1668,16 @@ static int l2cap_sar_segment_sdu(struct l2cap_chan *chan, struct msghdr *msg, si
        return size;
 }
 
-int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len)
+int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
+                                                               u32 priority)
 {
        struct sk_buff *skb;
-       u16 control;
+       u32 control;
        int err;
 
        /* Connectionless channel */
        if (chan->chan_type == L2CAP_CHAN_CONN_LESS) {
-               skb = l2cap_create_connless_pdu(chan, msg, len);
+               skb = l2cap_create_connless_pdu(chan, msg, len, priority);
                if (IS_ERR(skb))
                        return PTR_ERR(skb);
 
@@ -1640,7 +1692,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len)
                        return -EMSGSIZE;
 
                /* Create a basic PDU */
-               skb = l2cap_create_basic_pdu(chan, msg, len);
+               skb = l2cap_create_basic_pdu(chan, msg, len, priority);
                if (IS_ERR(skb))
                        return PTR_ERR(skb);
 
@@ -1652,7 +1704,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len)
        case L2CAP_MODE_STREAMING:
                /* Entire SDU fits into one PDU */
                if (len <= chan->remote_mps) {
-                       control = L2CAP_SDU_UNSEGMENTED;
+                       control = __set_ctrl_sar(chan, L2CAP_SAR_UNSEGMENTED);
                        skb = l2cap_create_iframe_pdu(chan, msg, len, control,
                                                                        0);
                        if (IS_ERR(skb))
@@ -1850,6 +1902,37 @@ static void l2cap_add_conf_opt(void **ptr, u8 type, u8 len, unsigned long val)
        *ptr += L2CAP_CONF_OPT_SIZE + len;
 }
 
+static void l2cap_add_opt_efs(void **ptr, struct l2cap_chan *chan)
+{
+       struct l2cap_conf_efs efs;
+
+       switch(chan->mode) {
+       case L2CAP_MODE_ERTM:
+               efs.id          = chan->local_id;
+               efs.stype       = chan->local_stype;
+               efs.msdu        = cpu_to_le16(chan->local_msdu);
+               efs.sdu_itime   = cpu_to_le32(chan->local_sdu_itime);
+               efs.acc_lat     = cpu_to_le32(L2CAP_DEFAULT_ACC_LAT);
+               efs.flush_to    = cpu_to_le32(L2CAP_DEFAULT_FLUSH_TO);
+               break;
+
+       case L2CAP_MODE_STREAMING:
+               efs.id          = 1;
+               efs.stype       = L2CAP_SERV_BESTEFFORT;
+               efs.msdu        = cpu_to_le16(chan->local_msdu);
+               efs.sdu_itime   = cpu_to_le32(chan->local_sdu_itime);
+               efs.acc_lat     = 0;
+               efs.flush_to    = 0;
+               break;
+
+       default:
+               return;
+       }
+
+       l2cap_add_conf_opt(ptr, L2CAP_CONF_EFS, sizeof(efs),
+                                                       (unsigned long) &efs);
+}
+
 static void l2cap_ack_timeout(unsigned long arg)
 {
        struct l2cap_chan *chan = (void *) arg;
@@ -1896,11 +1979,36 @@ static inline __u8 l2cap_select_mode(__u8 mode, __u16 remote_feat_mask)
        }
 }
 
+static inline bool __l2cap_ews_supported(struct l2cap_chan *chan)
+{
+       return enable_hs && chan->conn->feat_mask & L2CAP_FEAT_EXT_WINDOW;
+}
+
+static inline bool __l2cap_efs_supported(struct l2cap_chan *chan)
+{
+       return enable_hs && chan->conn->feat_mask & L2CAP_FEAT_EXT_FLOW;
+}
+
+static inline void l2cap_txwin_setup(struct l2cap_chan *chan)
+{
+       if (chan->tx_win > L2CAP_DEFAULT_TX_WINDOW &&
+                                               __l2cap_ews_supported(chan)) {
+               /* use extended control field */
+               set_bit(FLAG_EXT_CTRL, &chan->flags);
+               chan->tx_win_max = L2CAP_DEFAULT_EXT_WINDOW;
+       } else {
+               chan->tx_win = min_t(u16, chan->tx_win,
+                                               L2CAP_DEFAULT_TX_WINDOW);
+               chan->tx_win_max = L2CAP_DEFAULT_TX_WINDOW;
+       }
+}
+
 static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data)
 {
        struct l2cap_conf_req *req = data;
        struct l2cap_conf_rfc rfc = { .mode = chan->mode };
        void *ptr = req->data;
+       u16 size;
 
        BT_DBG("chan %p", chan);
 
@@ -1913,6 +2021,9 @@ static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data)
                if (test_bit(CONF_STATE2_DEVICE, &chan->conf_state))
                        break;
 
+               if (__l2cap_efs_supported(chan))
+                       set_bit(FLAG_EFS_ENABLE, &chan->flags);
+
                /* fall through */
        default:
                chan->mode = l2cap_select_mode(rfc.mode, chan->conn->feat_mask);
@@ -1942,17 +2053,27 @@ done:
 
        case L2CAP_MODE_ERTM:
                rfc.mode            = L2CAP_MODE_ERTM;
-               rfc.txwin_size      = chan->tx_win;
                rfc.max_transmit    = chan->max_tx;
                rfc.retrans_timeout = 0;
                rfc.monitor_timeout = 0;
-               rfc.max_pdu_size    = cpu_to_le16(L2CAP_DEFAULT_MAX_PDU_SIZE);
-               if (L2CAP_DEFAULT_MAX_PDU_SIZE > chan->conn->mtu - 10)
-                       rfc.max_pdu_size = cpu_to_le16(chan->conn->mtu - 10);
+
+               size = min_t(u16, L2CAP_DEFAULT_MAX_PDU_SIZE, chan->conn->mtu -
+                                               L2CAP_EXT_HDR_SIZE -
+                                               L2CAP_SDULEN_SIZE -
+                                               L2CAP_FCS_SIZE);
+               rfc.max_pdu_size = cpu_to_le16(size);
+
+               l2cap_txwin_setup(chan);
+
+               rfc.txwin_size = min_t(u16, chan->tx_win,
+                                               L2CAP_DEFAULT_TX_WINDOW);
 
                l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
                                                        (unsigned long) &rfc);
 
+               if (test_bit(FLAG_EFS_ENABLE, &chan->flags))
+                       l2cap_add_opt_efs(&ptr, chan);
+
                if (!(chan->conn->feat_mask & L2CAP_FEAT_FCS))
                        break;
 
@@ -1961,6 +2082,10 @@ done:
                        chan->fcs = L2CAP_FCS_NONE;
                        l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, chan->fcs);
                }
+
+               if (test_bit(FLAG_EXT_CTRL, &chan->flags))
+                       l2cap_add_conf_opt(&ptr, L2CAP_CONF_EWS, 2,
+                                                               chan->tx_win);
                break;
 
        case L2CAP_MODE_STREAMING:
@@ -1969,13 +2094,19 @@ done:
                rfc.max_transmit    = 0;
                rfc.retrans_timeout = 0;
                rfc.monitor_timeout = 0;
-               rfc.max_pdu_size    = cpu_to_le16(L2CAP_DEFAULT_MAX_PDU_SIZE);
-               if (L2CAP_DEFAULT_MAX_PDU_SIZE > chan->conn->mtu - 10)
-                       rfc.max_pdu_size = cpu_to_le16(chan->conn->mtu - 10);
+
+               size = min_t(u16, L2CAP_DEFAULT_MAX_PDU_SIZE, chan->conn->mtu -
+                                               L2CAP_EXT_HDR_SIZE -
+                                               L2CAP_SDULEN_SIZE -
+                                               L2CAP_FCS_SIZE);
+               rfc.max_pdu_size = cpu_to_le16(size);
 
                l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
                                                        (unsigned long) &rfc);
 
+               if (test_bit(FLAG_EFS_ENABLE, &chan->flags))
+                       l2cap_add_opt_efs(&ptr, chan);
+
                if (!(chan->conn->feat_mask & L2CAP_FEAT_FCS))
                        break;
 
@@ -2002,8 +2133,11 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data)
        int type, hint, olen;
        unsigned long val;
        struct l2cap_conf_rfc rfc = { .mode = L2CAP_MODE_BASIC };
+       struct l2cap_conf_efs efs;
+       u8 remote_efs = 0;
        u16 mtu = L2CAP_DEFAULT_MTU;
        u16 result = L2CAP_CONF_SUCCESS;
+       u16 size;
 
        BT_DBG("chan %p", chan);
 
@@ -2033,7 +2167,22 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data)
                case L2CAP_CONF_FCS:
                        if (val == L2CAP_FCS_NONE)
                                set_bit(CONF_NO_FCS_RECV, &chan->conf_state);
+                       break;
+
+               case L2CAP_CONF_EFS:
+                       remote_efs = 1;
+                       if (olen == sizeof(efs))
+                               memcpy(&efs, (void *) val, olen);
+                       break;
+
+               case L2CAP_CONF_EWS:
+                       if (!enable_hs)
+                               return -ECONNREFUSED;
 
+                       set_bit(FLAG_EXT_CTRL, &chan->flags);
+                       set_bit(CONF_EWS_RECV, &chan->conf_state);
+                       chan->tx_win_max = L2CAP_DEFAULT_EXT_WINDOW;
+                       chan->remote_tx_win = val;
                        break;
 
                default:
@@ -2058,6 +2207,13 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data)
                        break;
                }
 
+               if (remote_efs) {
+                       if (__l2cap_efs_supported(chan))
+                               set_bit(FLAG_EFS_ENABLE, &chan->flags);
+                       else
+                               return -ECONNREFUSED;
+               }
+
                if (chan->mode != rfc.mode)
                        return -ECONNREFUSED;
 
@@ -2076,7 +2232,6 @@ done:
                                        sizeof(rfc), (unsigned long) &rfc);
        }
 
-
        if (result == L2CAP_CONF_SUCCESS) {
                /* Configure output options and let the other side know
                 * which ones we don't like. */
@@ -2089,6 +2244,26 @@ done:
                }
                l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->omtu);
 
+               if (remote_efs) {
+                       if (chan->local_stype != L2CAP_SERV_NOTRAFIC &&
+                                       efs.stype != L2CAP_SERV_NOTRAFIC &&
+                                       efs.stype != chan->local_stype) {
+
+                               result = L2CAP_CONF_UNACCEPT;
+
+                               if (chan->num_conf_req >= 1)
+                                       return -ECONNREFUSED;
+
+                               l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS,
+                                                       sizeof(efs),
+                                                       (unsigned long) &efs);
+                       } else {
+                               /* Send PENDING Conf Rsp */
+                               result = L2CAP_CONF_PENDING;
+                               set_bit(CONF_LOC_CONF_PEND, &chan->conf_state);
+                       }
+               }
+
                switch (rfc.mode) {
                case L2CAP_MODE_BASIC:
                        chan->fcs = L2CAP_FCS_NONE;
@@ -2096,13 +2271,20 @@ done:
                        break;
 
                case L2CAP_MODE_ERTM:
-                       chan->remote_tx_win = rfc.txwin_size;
-                       chan->remote_max_tx = rfc.max_transmit;
+                       if (!test_bit(CONF_EWS_RECV, &chan->conf_state))
+                               chan->remote_tx_win = rfc.txwin_size;
+                       else
+                               rfc.txwin_size = L2CAP_DEFAULT_TX_WINDOW;
 
-                       if (le16_to_cpu(rfc.max_pdu_size) > chan->conn->mtu - 10)
-                               rfc.max_pdu_size = cpu_to_le16(chan->conn->mtu - 10);
+                       chan->remote_max_tx = rfc.max_transmit;
 
-                       chan->remote_mps = le16_to_cpu(rfc.max_pdu_size);
+                       size = min_t(u16, le16_to_cpu(rfc.max_pdu_size),
+                                               chan->conn->mtu -
+                                               L2CAP_EXT_HDR_SIZE -
+                                               L2CAP_SDULEN_SIZE -
+                                               L2CAP_FCS_SIZE);
+                       rfc.max_pdu_size = cpu_to_le16(size);
+                       chan->remote_mps = size;
 
                        rfc.retrans_timeout =
                                le16_to_cpu(L2CAP_DEFAULT_RETRANS_TO);
@@ -2114,13 +2296,29 @@ done:
                        l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
                                        sizeof(rfc), (unsigned long) &rfc);
 
+                       if (test_bit(FLAG_EFS_ENABLE, &chan->flags)) {
+                               chan->remote_id = efs.id;
+                               chan->remote_stype = efs.stype;
+                               chan->remote_msdu = le16_to_cpu(efs.msdu);
+                               chan->remote_flush_to =
+                                               le32_to_cpu(efs.flush_to);
+                               chan->remote_acc_lat =
+                                               le32_to_cpu(efs.acc_lat);
+                               chan->remote_sdu_itime =
+                                       le32_to_cpu(efs.sdu_itime);
+                               l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS,
+                                       sizeof(efs), (unsigned long) &efs);
+                       }
                        break;
 
                case L2CAP_MODE_STREAMING:
-                       if (le16_to_cpu(rfc.max_pdu_size) > chan->conn->mtu - 10)
-                               rfc.max_pdu_size = cpu_to_le16(chan->conn->mtu - 10);
-
-                       chan->remote_mps = le16_to_cpu(rfc.max_pdu_size);
+                       size = min_t(u16, le16_to_cpu(rfc.max_pdu_size),
+                                               chan->conn->mtu -
+                                               L2CAP_EXT_HDR_SIZE -
+                                               L2CAP_SDULEN_SIZE -
+                                               L2CAP_FCS_SIZE);
+                       rfc.max_pdu_size = cpu_to_le16(size);
+                       chan->remote_mps = size;
 
                        set_bit(CONF_MODE_DONE, &chan->conf_state);
 
@@ -2153,6 +2351,7 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, voi
        int type, olen;
        unsigned long val;
        struct l2cap_conf_rfc rfc;
+       struct l2cap_conf_efs efs;
 
        BT_DBG("chan %p, rsp %p, len %d, req %p", chan, rsp, len, data);
 
@@ -2188,6 +2387,26 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, voi
                        l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
                                        sizeof(rfc), (unsigned long) &rfc);
                        break;
+
+               case L2CAP_CONF_EWS:
+                       chan->tx_win = min_t(u16, val,
+                                               L2CAP_DEFAULT_EXT_WINDOW);
+                       l2cap_add_conf_opt(&ptr, L2CAP_CONF_EWS, 2,
+                                                       chan->tx_win);
+                       break;
+
+               case L2CAP_CONF_EFS:
+                       if (olen == sizeof(efs))
+                               memcpy(&efs, (void *)val, olen);
+
+                       if (chan->local_stype != L2CAP_SERV_NOTRAFIC &&
+                                       efs.stype != L2CAP_SERV_NOTRAFIC &&
+                                       efs.stype != chan->local_stype)
+                               return -ECONNREFUSED;
+
+                       l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS,
+                                       sizeof(efs), (unsigned long) &efs);
+                       break;
                }
        }
 
@@ -2196,13 +2415,23 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, voi
 
        chan->mode = rfc.mode;
 
-       if (*result == L2CAP_CONF_SUCCESS) {
+       if (*result == L2CAP_CONF_SUCCESS || *result == L2CAP_CONF_PENDING) {
                switch (rfc.mode) {
                case L2CAP_MODE_ERTM:
                        chan->retrans_timeout = le16_to_cpu(rfc.retrans_timeout);
                        chan->monitor_timeout = le16_to_cpu(rfc.monitor_timeout);
                        chan->mps    = le16_to_cpu(rfc.max_pdu_size);
+
+                       if (test_bit(FLAG_EFS_ENABLE, &chan->flags)) {
+                               chan->local_msdu = le16_to_cpu(efs.msdu);
+                               chan->local_sdu_itime =
+                                               le32_to_cpu(efs.sdu_itime);
+                               chan->local_acc_lat = le32_to_cpu(efs.acc_lat);
+                               chan->local_flush_to =
+                                               le32_to_cpu(efs.flush_to);
+                       }
                        break;
+
                case L2CAP_MODE_STREAMING:
                        chan->mps    = le16_to_cpu(rfc.max_pdu_size);
                }
@@ -2330,7 +2559,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
        /* Check if the ACL is secure enough (if not SDP) */
        if (psm != cpu_to_le16(0x0001) &&
                                !hci_conn_check_link_mode(conn->hcon)) {
-               conn->disc_reason = 0x05;
+               conn->disc_reason = HCI_ERROR_AUTH_FAILURE;
                result = L2CAP_CR_SEC_BLOCK;
                goto response;
        }
@@ -2602,6 +2831,21 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
                chan->num_conf_req++;
        }
 
+       /* Got Conf Rsp PENDING from remote side and asume we sent
+          Conf Rsp PENDING in the code above */
+       if (test_bit(CONF_REM_CONF_PEND, &chan->conf_state) &&
+                       test_bit(CONF_LOC_CONF_PEND, &chan->conf_state)) {
+
+               /* check compatibility */
+
+               clear_bit(CONF_LOC_CONF_PEND, &chan->conf_state);
+               set_bit(CONF_OUTPUT_DONE, &chan->conf_state);
+
+               l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP,
+                                       l2cap_build_conf_rsp(chan, rsp,
+                                       L2CAP_CONF_SUCCESS, 0x0000), rsp);
+       }
+
 unlock:
        bh_unlock_sock(sk);
        return 0;
@@ -2631,8 +2875,33 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
        switch (result) {
        case L2CAP_CONF_SUCCESS:
                l2cap_conf_rfc_get(chan, rsp->data, len);
+               clear_bit(CONF_REM_CONF_PEND, &chan->conf_state);
                break;
 
+       case L2CAP_CONF_PENDING:
+               set_bit(CONF_REM_CONF_PEND, &chan->conf_state);
+
+               if (test_bit(CONF_LOC_CONF_PEND, &chan->conf_state)) {
+                       char buf[64];
+
+                       len = l2cap_parse_conf_rsp(chan, rsp->data, len,
+                                                               buf, &result);
+                       if (len < 0) {
+                               l2cap_send_disconn_req(conn, chan, ECONNRESET);
+                               goto done;
+                       }
+
+                       /* check compatibility */
+
+                       clear_bit(CONF_LOC_CONF_PEND, &chan->conf_state);
+                       set_bit(CONF_OUTPUT_DONE, &chan->conf_state);
+
+                       l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP,
+                                               l2cap_build_conf_rsp(chan, buf,
+                                               L2CAP_CONF_SUCCESS, 0x0000), buf);
+               }
+               goto done;
+
        case L2CAP_CONF_UNACCEPT:
                if (chan->num_conf_rsp <= L2CAP_CONF_MAX_CONF_RSP) {
                        char req[64];
@@ -2782,15 +3051,25 @@ static inline int l2cap_information_req(struct l2cap_conn *conn, struct l2cap_cm
                if (!disable_ertm)
                        feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING
                                                         | L2CAP_FEAT_FCS;
+               if (enable_hs)
+                       feat_mask |= L2CAP_FEAT_EXT_FLOW
+                                               | L2CAP_FEAT_EXT_WINDOW;
+
                put_unaligned_le32(feat_mask, rsp->data);
                l2cap_send_cmd(conn, cmd->ident,
                                        L2CAP_INFO_RSP, sizeof(buf), buf);
        } else if (type == L2CAP_IT_FIXED_CHAN) {
                u8 buf[12];
                struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) buf;
+
+               if (enable_hs)
+                       l2cap_fixed_chan[0] |= L2CAP_FC_A2MP;
+               else
+                       l2cap_fixed_chan[0] &= ~L2CAP_FC_A2MP;
+
                rsp->type   = cpu_to_le16(L2CAP_IT_FIXED_CHAN);
                rsp->result = cpu_to_le16(L2CAP_IR_SUCCESS);
-               memcpy(buf + 4, l2cap_fixed_chan, 8);
+               memcpy(rsp->data, l2cap_fixed_chan, sizeof(l2cap_fixed_chan));
                l2cap_send_cmd(conn, cmd->ident,
                                        L2CAP_INFO_RSP, sizeof(buf), buf);
        } else {
@@ -2857,6 +3136,165 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cm
        return 0;
 }
 
+static inline int l2cap_create_channel_req(struct l2cap_conn *conn,
+                                       struct l2cap_cmd_hdr *cmd, u16 cmd_len,
+                                       void *data)
+{
+       struct l2cap_create_chan_req *req = data;
+       struct l2cap_create_chan_rsp rsp;
+       u16 psm, scid;
+
+       if (cmd_len != sizeof(*req))
+               return -EPROTO;
+
+       if (!enable_hs)
+               return -EINVAL;
+
+       psm = le16_to_cpu(req->psm);
+       scid = le16_to_cpu(req->scid);
+
+       BT_DBG("psm %d, scid %d, amp_id %d", psm, scid, req->amp_id);
+
+       /* Placeholder: Always reject */
+       rsp.dcid = 0;
+       rsp.scid = cpu_to_le16(scid);
+       rsp.result = L2CAP_CR_NO_MEM;
+       rsp.status = L2CAP_CS_NO_INFO;
+
+       l2cap_send_cmd(conn, cmd->ident, L2CAP_CREATE_CHAN_RSP,
+                      sizeof(rsp), &rsp);
+
+       return 0;
+}
+
+static inline int l2cap_create_channel_rsp(struct l2cap_conn *conn,
+                                       struct l2cap_cmd_hdr *cmd, void *data)
+{
+       BT_DBG("conn %p", conn);
+
+       return l2cap_connect_rsp(conn, cmd, data);
+}
+
+static void l2cap_send_move_chan_rsp(struct l2cap_conn *conn, u8 ident,
+                                                       u16 icid, u16 result)
+{
+       struct l2cap_move_chan_rsp rsp;
+
+       BT_DBG("icid %d, result %d", icid, result);
+
+       rsp.icid = cpu_to_le16(icid);
+       rsp.result = cpu_to_le16(result);
+
+       l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_RSP, sizeof(rsp), &rsp);
+}
+
+static void l2cap_send_move_chan_cfm(struct l2cap_conn *conn,
+                               struct l2cap_chan *chan, u16 icid, u16 result)
+{
+       struct l2cap_move_chan_cfm cfm;
+       u8 ident;
+
+       BT_DBG("icid %d, result %d", icid, result);
+
+       ident = l2cap_get_ident(conn);
+       if (chan)
+               chan->ident = ident;
+
+       cfm.icid = cpu_to_le16(icid);
+       cfm.result = cpu_to_le16(result);
+
+       l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_CFM, sizeof(cfm), &cfm);
+}
+
+static void l2cap_send_move_chan_cfm_rsp(struct l2cap_conn *conn, u8 ident,
+                                                               u16 icid)
+{
+       struct l2cap_move_chan_cfm_rsp rsp;
+
+       BT_DBG("icid %d", icid);
+
+       rsp.icid = cpu_to_le16(icid);
+       l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_CFM_RSP, sizeof(rsp), &rsp);
+}
+
+static inline int l2cap_move_channel_req(struct l2cap_conn *conn,
+                       struct l2cap_cmd_hdr *cmd, u16 cmd_len, void *data)
+{
+       struct l2cap_move_chan_req *req = data;
+       u16 icid = 0;
+       u16 result = L2CAP_MR_NOT_ALLOWED;
+
+       if (cmd_len != sizeof(*req))
+               return -EPROTO;
+
+       icid = le16_to_cpu(req->icid);
+
+       BT_DBG("icid %d, dest_amp_id %d", icid, req->dest_amp_id);
+
+       if (!enable_hs)
+               return -EINVAL;
+
+       /* Placeholder: Always refuse */
+       l2cap_send_move_chan_rsp(conn, cmd->ident, icid, result);
+
+       return 0;
+}
+
+static inline int l2cap_move_channel_rsp(struct l2cap_conn *conn,
+                       struct l2cap_cmd_hdr *cmd, u16 cmd_len, void *data)
+{
+       struct l2cap_move_chan_rsp *rsp = data;
+       u16 icid, result;
+
+       if (cmd_len != sizeof(*rsp))
+               return -EPROTO;
+
+       icid = le16_to_cpu(rsp->icid);
+       result = le16_to_cpu(rsp->result);
+
+       BT_DBG("icid %d, result %d", icid, result);
+
+       /* Placeholder: Always unconfirmed */
+       l2cap_send_move_chan_cfm(conn, NULL, icid, L2CAP_MC_UNCONFIRMED);
+
+       return 0;
+}
+
+static inline int l2cap_move_channel_confirm(struct l2cap_conn *conn,
+                       struct l2cap_cmd_hdr *cmd, u16 cmd_len, void *data)
+{
+       struct l2cap_move_chan_cfm *cfm = data;
+       u16 icid, result;
+
+       if (cmd_len != sizeof(*cfm))
+               return -EPROTO;
+
+       icid = le16_to_cpu(cfm->icid);
+       result = le16_to_cpu(cfm->result);
+
+       BT_DBG("icid %d, result %d", icid, result);
+
+       l2cap_send_move_chan_cfm_rsp(conn, cmd->ident, icid);
+
+       return 0;
+}
+
+static inline int l2cap_move_channel_confirm_rsp(struct l2cap_conn *conn,
+                       struct l2cap_cmd_hdr *cmd, u16 cmd_len, void *data)
+{
+       struct l2cap_move_chan_cfm_rsp *rsp = data;
+       u16 icid;
+
+       if (cmd_len != sizeof(*rsp))
+               return -EPROTO;
+
+       icid = le16_to_cpu(rsp->icid);
+
+       BT_DBG("icid %d", icid);
+
+       return 0;
+}
+
 static inline int l2cap_check_conn_param(u16 min, u16 max, u16 latency,
                                                        u16 to_multiplier)
 {
@@ -2969,6 +3407,30 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
                err = l2cap_information_rsp(conn, cmd, data);
                break;
 
+       case L2CAP_CREATE_CHAN_REQ:
+               err = l2cap_create_channel_req(conn, cmd, cmd_len, data);
+               break;
+
+       case L2CAP_CREATE_CHAN_RSP:
+               err = l2cap_create_channel_rsp(conn, cmd, data);
+               break;
+
+       case L2CAP_MOVE_CHAN_REQ:
+               err = l2cap_move_channel_req(conn, cmd, cmd_len, data);
+               break;
+
+       case L2CAP_MOVE_CHAN_RSP:
+               err = l2cap_move_channel_rsp(conn, cmd, cmd_len, data);
+               break;
+
+       case L2CAP_MOVE_CHAN_CFM:
+               err = l2cap_move_channel_confirm(conn, cmd, cmd_len, data);
+               break;
+
+       case L2CAP_MOVE_CHAN_CFM_RSP:
+               err = l2cap_move_channel_confirm_rsp(conn, cmd, cmd_len, data);
+               break;
+
        default:
                BT_ERR("Unknown BR/EDR signaling command 0x%2.2x", cmd->code);
                err = -EINVAL;
@@ -3047,10 +3509,15 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn,
 static int l2cap_check_fcs(struct l2cap_chan *chan,  struct sk_buff *skb)
 {
        u16 our_fcs, rcv_fcs;
-       int hdr_size = L2CAP_HDR_SIZE + 2;
+       int hdr_size;
+
+       if (test_bit(FLAG_EXT_CTRL, &chan->flags))
+               hdr_size = L2CAP_EXT_HDR_SIZE;
+       else
+               hdr_size = L2CAP_ENH_HDR_SIZE;
 
        if (chan->fcs == L2CAP_FCS_CRC16) {
-               skb_trim(skb, skb->len - 2);
+               skb_trim(skb, skb->len - L2CAP_FCS_SIZE);
                rcv_fcs = get_unaligned_le16(skb->data + skb->len);
                our_fcs = crc16(0, skb->data - hdr_size, skb->len + hdr_size);
 
@@ -3062,14 +3529,14 @@ static int l2cap_check_fcs(struct l2cap_chan *chan,  struct sk_buff *skb)
 
 static inline void l2cap_send_i_or_rr_or_rnr(struct l2cap_chan *chan)
 {
-       u16 control = 0;
+       u32 control = 0;
 
        chan->frames_sent = 0;
 
-       control |= chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT;
+       control |= __set_reqseq(chan, chan->buffer_seq);
 
        if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
-               control |= L2CAP_SUPER_RCV_NOT_READY;
+               control |= __set_ctrl_super(chan, L2CAP_SUPER_RNR);
                l2cap_send_sframe(chan, control);
                set_bit(CONN_RNR_SENT, &chan->conn_state);
        }
@@ -3081,12 +3548,12 @@ static inline void l2cap_send_i_or_rr_or_rnr(struct l2cap_chan *chan)
 
        if (!test_bit(CONN_LOCAL_BUSY, &chan->conn_state) &&
                        chan->frames_sent == 0) {
-               control |= L2CAP_SUPER_RCV_READY;
+               control |= __set_ctrl_super(chan, L2CAP_SUPER_RR);
                l2cap_send_sframe(chan, control);
        }
 }
 
-static int l2cap_add_to_srej_queue(struct l2cap_chan *chan, struct sk_buff *skb, u8 tx_seq, u8 sar)
+static int l2cap_add_to_srej_queue(struct l2cap_chan *chan, struct sk_buff *skb, u16 tx_seq, u8 sar)
 {
        struct sk_buff *next_skb;
        int tx_seq_offset, next_tx_seq_offset;
@@ -3100,18 +3567,14 @@ static int l2cap_add_to_srej_queue(struct l2cap_chan *chan, struct sk_buff *skb,
                return 0;
        }
 
-       tx_seq_offset = (tx_seq - chan->buffer_seq) % 64;
-       if (tx_seq_offset < 0)
-               tx_seq_offset += 64;
+       tx_seq_offset = __seq_offset(chan, tx_seq, chan->buffer_seq);
 
        do {
                if (bt_cb(next_skb)->tx_seq == tx_seq)
                        return -EINVAL;
 
-               next_tx_seq_offset = (bt_cb(next_skb)->tx_seq -
-                                               chan->buffer_seq) % 64;
-               if (next_tx_seq_offset < 0)
-                       next_tx_seq_offset += 64;
+               next_tx_seq_offset = __seq_offset(chan,
+                               bt_cb(next_skb)->tx_seq, chan->buffer_seq);
 
                if (next_tx_seq_offset > tx_seq_offset) {
                        __skb_queue_before(&chan->srej_q, next_skb, skb);
@@ -3147,24 +3610,24 @@ static void append_skb_frag(struct sk_buff *skb,
        skb->truesize += new_frag->truesize;
 }
 
-static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u16 control)
+static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u32 control)
 {
        int err = -EINVAL;
 
-       switch (control & L2CAP_CTRL_SAR) {
-       case L2CAP_SDU_UNSEGMENTED:
+       switch (__get_ctrl_sar(chan, control)) {
+       case L2CAP_SAR_UNSEGMENTED:
                if (chan->sdu)
                        break;
 
                err = chan->ops->recv(chan->data, skb);
                break;
 
-       case L2CAP_SDU_START:
+       case L2CAP_SAR_START:
                if (chan->sdu)
                        break;
 
                chan->sdu_len = get_unaligned_le16(skb->data);
-               skb_pull(skb, 2);
+               skb_pull(skb, L2CAP_SDULEN_SIZE);
 
                if (chan->sdu_len > chan->imtu) {
                        err = -EMSGSIZE;
@@ -3181,7 +3644,7 @@ static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u1
                err = 0;
                break;
 
-       case L2CAP_SDU_CONTINUE:
+       case L2CAP_SAR_CONTINUE:
                if (!chan->sdu)
                        break;
 
@@ -3195,7 +3658,7 @@ static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u1
                err = 0;
                break;
 
-       case L2CAP_SDU_END:
+       case L2CAP_SAR_END:
                if (!chan->sdu)
                        break;
 
@@ -3230,14 +3693,14 @@ static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u1
 
 static void l2cap_ertm_enter_local_busy(struct l2cap_chan *chan)
 {
-       u16 control;
+       u32 control;
 
        BT_DBG("chan %p, Enter local busy", chan);
 
        set_bit(CONN_LOCAL_BUSY, &chan->conn_state);
 
-       control = chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT;
-       control |= L2CAP_SUPER_RCV_NOT_READY;
+       control = __set_reqseq(chan, chan->buffer_seq);
+       control |= __set_ctrl_super(chan, L2CAP_SUPER_RNR);
        l2cap_send_sframe(chan, control);
 
        set_bit(CONN_RNR_SENT, &chan->conn_state);
@@ -3247,13 +3710,14 @@ static void l2cap_ertm_enter_local_busy(struct l2cap_chan *chan)
 
 static void l2cap_ertm_exit_local_busy(struct l2cap_chan *chan)
 {
-       u16 control;
+       u32 control;
 
        if (!test_bit(CONN_RNR_SENT, &chan->conn_state))
                goto done;
 
-       control = chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT;
-       control |= L2CAP_SUPER_RCV_READY | L2CAP_CTRL_POLL;
+       control = __set_reqseq(chan, chan->buffer_seq);
+       control |= __set_ctrl_poll(chan);
+       control |= __set_ctrl_super(chan, L2CAP_SUPER_RR);
        l2cap_send_sframe(chan, control);
        chan->retry_count = 1;
 
@@ -3279,10 +3743,10 @@ void l2cap_chan_busy(struct l2cap_chan *chan, int busy)
        }
 }
 
-static void l2cap_check_srej_gap(struct l2cap_chan *chan, u8 tx_seq)
+static void l2cap_check_srej_gap(struct l2cap_chan *chan, u16 tx_seq)
 {
        struct sk_buff *skb;
-       u16 control;
+       u32 control;
 
        while ((skb = skb_peek(&chan->srej_q)) &&
                        !test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
@@ -3292,7 +3756,7 @@ static void l2cap_check_srej_gap(struct l2cap_chan *chan, u8 tx_seq)
                        break;
 
                skb = skb_dequeue(&chan->srej_q);
-               control = bt_cb(skb)->sar << L2CAP_CTRL_SAR_SHIFT;
+               control = __set_ctrl_sar(chan, bt_cb(skb)->sar);
                err = l2cap_reassemble_sdu(chan, skb, control);
 
                if (err < 0) {
@@ -3300,16 +3764,15 @@ static void l2cap_check_srej_gap(struct l2cap_chan *chan, u8 tx_seq)
                        break;
                }
 
-               chan->buffer_seq_srej =
-                       (chan->buffer_seq_srej + 1) % 64;
-               tx_seq = (tx_seq + 1) % 64;
+               chan->buffer_seq_srej = __next_seq(chan, chan->buffer_seq_srej);
+               tx_seq = __next_seq(chan, tx_seq);
        }
 }
 
-static void l2cap_resend_srejframe(struct l2cap_chan *chan, u8 tx_seq)
+static void l2cap_resend_srejframe(struct l2cap_chan *chan, u16 tx_seq)
 {
        struct srej_list *l, *tmp;
-       u16 control;
+       u32 control;
 
        list_for_each_entry_safe(l, tmp, &chan->srej_l, list) {
                if (l->tx_seq == tx_seq) {
@@ -3317,45 +3780,48 @@ static void l2cap_resend_srejframe(struct l2cap_chan *chan, u8 tx_seq)
                        kfree(l);
                        return;
                }
-               control = L2CAP_SUPER_SELECT_REJECT;
-               control |= l->tx_seq << L2CAP_CTRL_REQSEQ_SHIFT;
+               control = __set_ctrl_super(chan, L2CAP_SUPER_SREJ);
+               control |= __set_reqseq(chan, l->tx_seq);
                l2cap_send_sframe(chan, control);
                list_del(&l->list);
                list_add_tail(&l->list, &chan->srej_l);
        }
 }
 
-static void l2cap_send_srejframe(struct l2cap_chan *chan, u8 tx_seq)
+static void l2cap_send_srejframe(struct l2cap_chan *chan, u16 tx_seq)
 {
        struct srej_list *new;
-       u16 control;
+       u32 control;
 
        while (tx_seq != chan->expected_tx_seq) {
-               control = L2CAP_SUPER_SELECT_REJECT;
-               control |= chan->expected_tx_seq << L2CAP_CTRL_REQSEQ_SHIFT;
+               control = __set_ctrl_super(chan, L2CAP_SUPER_SREJ);
+               control |= __set_reqseq(chan, chan->expected_tx_seq);
                l2cap_send_sframe(chan, control);
 
                new = kzalloc(sizeof(struct srej_list), GFP_ATOMIC);
                new->tx_seq = chan->expected_tx_seq;
-               chan->expected_tx_seq = (chan->expected_tx_seq + 1) % 64;
+
+               chan->expected_tx_seq = __next_seq(chan, chan->expected_tx_seq);
+
                list_add_tail(&new->list, &chan->srej_l);
        }
-       chan->expected_tx_seq = (chan->expected_tx_seq + 1) % 64;
+
+       chan->expected_tx_seq = __next_seq(chan, chan->expected_tx_seq);
 }
 
-static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_control, struct sk_buff *skb)
+static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u32 rx_control, struct sk_buff *skb)
 {
-       u8 tx_seq = __get_txseq(rx_control);
-       u8 req_seq = __get_reqseq(rx_control);
-       u8 sar = rx_control >> L2CAP_CTRL_SAR_SHIFT;
+       u16 tx_seq = __get_txseq(chan, rx_control);
+       u16 req_seq = __get_reqseq(chan, rx_control);
+       u8 sar = __get_ctrl_sar(chan, rx_control);
        int tx_seq_offset, expected_tx_seq_offset;
        int num_to_ack = (chan->tx_win/6) + 1;
        int err = 0;
 
-       BT_DBG("chan %p len %d tx_seq %d rx_control 0x%4.4x", chan, skb->len,
+       BT_DBG("chan %p len %d tx_seq %d rx_control 0x%8.8x", chan, skb->len,
                                                        tx_seq, rx_control);
 
-       if (L2CAP_CTRL_FINAL & rx_control &&
+       if (__is_ctrl_final(chan, rx_control) &&
                        test_bit(CONN_WAIT_F, &chan->conn_state)) {
                __clear_monitor_timer(chan);
                if (chan->unacked_frames > 0)
@@ -3366,9 +3832,7 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont
        chan->expected_ack_seq = req_seq;
        l2cap_drop_acked_frames(chan);
 
-       tx_seq_offset = (tx_seq - chan->buffer_seq) % 64;
-       if (tx_seq_offset < 0)
-               tx_seq_offset += 64;
+       tx_seq_offset = __seq_offset(chan, tx_seq, chan->buffer_seq);
 
        /* invalid tx_seq */
        if (tx_seq_offset >= chan->tx_win) {
@@ -3416,10 +3880,8 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont
                        l2cap_send_srejframe(chan, tx_seq);
                }
        } else {
-               expected_tx_seq_offset =
-                       (chan->expected_tx_seq - chan->buffer_seq) % 64;
-               if (expected_tx_seq_offset < 0)
-                       expected_tx_seq_offset += 64;
+               expected_tx_seq_offset = __seq_offset(chan,
+                               chan->expected_tx_seq, chan->buffer_seq);
 
                /* duplicated tx_seq */
                if (tx_seq_offset < expected_tx_seq_offset)
@@ -3444,7 +3906,7 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont
        return 0;
 
 expected:
-       chan->expected_tx_seq = (chan->expected_tx_seq + 1) % 64;
+       chan->expected_tx_seq = __next_seq(chan, chan->expected_tx_seq);
 
        if (test_bit(CONN_SREJ_SENT, &chan->conn_state)) {
                bt_cb(skb)->tx_seq = tx_seq;
@@ -3454,13 +3916,14 @@ expected:
        }
 
        err = l2cap_reassemble_sdu(chan, skb, rx_control);
-       chan->buffer_seq = (chan->buffer_seq + 1) % 64;
+       chan->buffer_seq = __next_seq(chan, chan->buffer_seq);
+
        if (err < 0) {
                l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
                return err;
        }
 
-       if (rx_control & L2CAP_CTRL_FINAL) {
+       if (__is_ctrl_final(chan, rx_control)) {
                if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state))
                        l2cap_retransmit_frames(chan);
        }
@@ -3478,15 +3941,15 @@ drop:
        return 0;
 }
 
-static inline void l2cap_data_channel_rrframe(struct l2cap_chan *chan, u16 rx_control)
+static inline void l2cap_data_channel_rrframe(struct l2cap_chan *chan, u32 rx_control)
 {
-       BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, __get_reqseq(rx_control),
-                                               rx_control);
+       BT_DBG("chan %p, req_seq %d ctrl 0x%8.8x", chan,
+                               __get_reqseq(chan, rx_control), rx_control);
 
-       chan->expected_ack_seq = __get_reqseq(rx_control);
+       chan->expected_ack_seq = __get_reqseq(chan, rx_control);
        l2cap_drop_acked_frames(chan);
 
-       if (rx_control & L2CAP_CTRL_POLL) {
+       if (__is_ctrl_poll(chan, rx_control)) {
                set_bit(CONN_SEND_FBIT, &chan->conn_state);
                if (test_bit(CONN_SREJ_SENT, &chan->conn_state)) {
                        if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state) &&
@@ -3499,7 +3962,7 @@ static inline void l2cap_data_channel_rrframe(struct l2cap_chan *chan, u16 rx_co
                        l2cap_send_i_or_rr_or_rnr(chan);
                }
 
-       } else if (rx_control & L2CAP_CTRL_FINAL) {
+       } else if (__is_ctrl_final(chan, rx_control)) {
                clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
 
                if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state))
@@ -3518,18 +3981,18 @@ static inline void l2cap_data_channel_rrframe(struct l2cap_chan *chan, u16 rx_co
        }
 }
 
-static inline void l2cap_data_channel_rejframe(struct l2cap_chan *chan, u16 rx_control)
+static inline void l2cap_data_channel_rejframe(struct l2cap_chan *chan, u32 rx_control)
 {
-       u8 tx_seq = __get_reqseq(rx_control);
+       u16 tx_seq = __get_reqseq(chan, rx_control);
 
-       BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, tx_seq, rx_control);
+       BT_DBG("chan %p, req_seq %d ctrl 0x%8.8x", chan, tx_seq, rx_control);
 
        clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
 
        chan->expected_ack_seq = tx_seq;
        l2cap_drop_acked_frames(chan);
 
-       if (rx_control & L2CAP_CTRL_FINAL) {
+       if (__is_ctrl_final(chan, rx_control)) {
                if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state))
                        l2cap_retransmit_frames(chan);
        } else {
@@ -3539,15 +4002,15 @@ static inline void l2cap_data_channel_rejframe(struct l2cap_chan *chan, u16 rx_c
                        set_bit(CONN_REJ_ACT, &chan->conn_state);
        }
 }
-static inline void l2cap_data_channel_srejframe(struct l2cap_chan *chan, u16 rx_control)
+static inline void l2cap_data_channel_srejframe(struct l2cap_chan *chan, u32 rx_control)
 {
-       u8 tx_seq = __get_reqseq(rx_control);
+       u16 tx_seq = __get_reqseq(chan, rx_control);
 
-       BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, tx_seq, rx_control);
+       BT_DBG("chan %p, req_seq %d ctrl 0x%8.8x", chan, tx_seq, rx_control);
 
        clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
 
-       if (rx_control & L2CAP_CTRL_POLL) {
+       if (__is_ctrl_poll(chan, rx_control)) {
                chan->expected_ack_seq = tx_seq;
                l2cap_drop_acked_frames(chan);
 
@@ -3560,7 +4023,7 @@ static inline void l2cap_data_channel_srejframe(struct l2cap_chan *chan, u16 rx_
                        chan->srej_save_reqseq = tx_seq;
                        set_bit(CONN_SREJ_ACT, &chan->conn_state);
                }
-       } else if (rx_control & L2CAP_CTRL_FINAL) {
+       } else if (__is_ctrl_final(chan, rx_control)) {
                if (test_bit(CONN_SREJ_ACT, &chan->conn_state) &&
                                chan->srej_save_reqseq == tx_seq)
                        clear_bit(CONN_SREJ_ACT, &chan->conn_state);
@@ -3575,37 +4038,39 @@ static inline void l2cap_data_channel_srejframe(struct l2cap_chan *chan, u16 rx_
        }
 }
 
-static inline void l2cap_data_channel_rnrframe(struct l2cap_chan *chan, u16 rx_control)
+static inline void l2cap_data_channel_rnrframe(struct l2cap_chan *chan, u32 rx_control)
 {
-       u8 tx_seq = __get_reqseq(rx_control);
+       u16 tx_seq = __get_reqseq(chan, rx_control);
 
-       BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, tx_seq, rx_control);
+       BT_DBG("chan %p, req_seq %d ctrl 0x%8.8x", chan, tx_seq, rx_control);
 
        set_bit(CONN_REMOTE_BUSY, &chan->conn_state);
        chan->expected_ack_seq = tx_seq;
        l2cap_drop_acked_frames(chan);
 
-       if (rx_control & L2CAP_CTRL_POLL)
+       if (__is_ctrl_poll(chan, rx_control))
                set_bit(CONN_SEND_FBIT, &chan->conn_state);
 
        if (!test_bit(CONN_SREJ_SENT, &chan->conn_state)) {
                __clear_retrans_timer(chan);
-               if (rx_control & L2CAP_CTRL_POLL)
+               if (__is_ctrl_poll(chan, rx_control))
                        l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_FINAL);
                return;
        }
 
-       if (rx_control & L2CAP_CTRL_POLL)
+       if (__is_ctrl_poll(chan, rx_control)) {
                l2cap_send_srejtail(chan);
-       else
-               l2cap_send_sframe(chan, L2CAP_SUPER_RCV_READY);
+       } else {
+               rx_control = __set_ctrl_super(chan, L2CAP_SUPER_RR);
+               l2cap_send_sframe(chan, rx_control);
+       }
 }
 
-static inline int l2cap_data_channel_sframe(struct l2cap_chan *chan, u16 rx_control, struct sk_buff *skb)
+static inline int l2cap_data_channel_sframe(struct l2cap_chan *chan, u32 rx_control, struct sk_buff *skb)
 {
-       BT_DBG("chan %p rx_control 0x%4.4x len %d", chan, rx_control, skb->len);
+       BT_DBG("chan %p rx_control 0x%8.8x len %d", chan, rx_control, skb->len);
 
-       if (L2CAP_CTRL_FINAL & rx_control &&
+       if (__is_ctrl_final(chan, rx_control) &&
                        test_bit(CONN_WAIT_F, &chan->conn_state)) {
                __clear_monitor_timer(chan);
                if (chan->unacked_frames > 0)
@@ -3613,20 +4078,20 @@ static inline int l2cap_data_channel_sframe(struct l2cap_chan *chan, u16 rx_cont
                clear_bit(CONN_WAIT_F, &chan->conn_state);
        }
 
-       switch (rx_control & L2CAP_CTRL_SUPERVISE) {
-       case L2CAP_SUPER_RCV_READY:
+       switch (__get_ctrl_super(chan, rx_control)) {
+       case L2CAP_SUPER_RR:
                l2cap_data_channel_rrframe(chan, rx_control);
                break;
 
-       case L2CAP_SUPER_REJECT:
+       case L2CAP_SUPER_REJ:
                l2cap_data_channel_rejframe(chan, rx_control);
                break;
 
-       case L2CAP_SUPER_SELECT_REJECT:
+       case L2CAP_SUPER_SREJ:
                l2cap_data_channel_srejframe(chan, rx_control);
                break;
 
-       case L2CAP_SUPER_RCV_NOT_READY:
+       case L2CAP_SUPER_RNR:
                l2cap_data_channel_rnrframe(chan, rx_control);
                break;
        }
@@ -3638,12 +4103,12 @@ static inline int l2cap_data_channel_sframe(struct l2cap_chan *chan, u16 rx_cont
 static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb)
 {
        struct l2cap_chan *chan = l2cap_pi(sk)->chan;
-       u16 control;
-       u8 req_seq;
+       u32 control;
+       u16 req_seq;
        int len, next_tx_seq_offset, req_seq_offset;
 
-       control = get_unaligned_le16(skb->data);
-       skb_pull(skb, 2);
+       control = __get_control(chan, skb->data);
+       skb_pull(skb, __ctrl_size(chan));
        len = skb->len;
 
        /*
@@ -3654,26 +4119,23 @@ static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb)
        if (l2cap_check_fcs(chan, skb))
                goto drop;
 
-       if (__is_sar_start(control) && __is_iframe(control))
-               len -= 2;
+       if (__is_sar_start(chan, control) && !__is_sframe(chan, control))
+               len -= L2CAP_SDULEN_SIZE;
 
        if (chan->fcs == L2CAP_FCS_CRC16)
-               len -= 2;
+               len -= L2CAP_FCS_SIZE;
 
        if (len > chan->mps) {
                l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
                goto drop;
        }
 
-       req_seq = __get_reqseq(control);
-       req_seq_offset = (req_seq - chan->expected_ack_seq) % 64;
-       if (req_seq_offset < 0)
-               req_seq_offset += 64;
+       req_seq = __get_reqseq(chan, control);
 
-       next_tx_seq_offset =
-               (chan->next_tx_seq - chan->expected_ack_seq) % 64;
-       if (next_tx_seq_offset < 0)
-               next_tx_seq_offset += 64;
+       req_seq_offset = __seq_offset(chan, req_seq, chan->expected_ack_seq);
+
+       next_tx_seq_offset = __seq_offset(chan, chan->next_tx_seq,
+                                               chan->expected_ack_seq);
 
        /* check for invalid req-seq */
        if (req_seq_offset > next_tx_seq_offset) {
@@ -3681,7 +4143,7 @@ static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb)
                goto drop;
        }
 
-       if (__is_iframe(control)) {
+       if (!__is_sframe(chan, control)) {
                if (len < 0) {
                        l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
                        goto drop;
@@ -3709,8 +4171,8 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
 {
        struct l2cap_chan *chan;
        struct sock *sk = NULL;
-       u16 control;
-       u8 tx_seq;
+       u32 control;
+       u16 tx_seq;
        int len;
 
        chan = l2cap_get_chan_by_scid(conn, cid);
@@ -3751,23 +4213,23 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
                goto done;
 
        case L2CAP_MODE_STREAMING:
-               control = get_unaligned_le16(skb->data);
-               skb_pull(skb, 2);
+               control = __get_control(chan, skb->data);
+               skb_pull(skb, __ctrl_size(chan));
                len = skb->len;
 
                if (l2cap_check_fcs(chan, skb))
                        goto drop;
 
-               if (__is_sar_start(control))
-                       len -= 2;
+               if (__is_sar_start(chan, control))
+                       len -= L2CAP_SDULEN_SIZE;
 
                if (chan->fcs == L2CAP_FCS_CRC16)
-                       len -= 2;
+                       len -= L2CAP_FCS_SIZE;
 
-               if (len > chan->mps || len < 0 || __is_sframe(control))
+               if (len > chan->mps || len < 0 || __is_sframe(chan, control))
                        goto drop;
 
-               tx_seq = __get_txseq(control);
+               tx_seq = __get_txseq(chan, control);
 
                if (chan->expected_tx_seq != tx_seq) {
                        /* Frame(s) missing - must discard partial SDU */
@@ -3779,7 +4241,7 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
                        /* TODO: Notify userland of missing data */
                }
 
-               chan->expected_tx_seq = (tx_seq + 1) % 64;
+               chan->expected_tx_seq = __next_seq(chan, tx_seq);
 
                if (l2cap_reassemble_sdu(chan, skb, control) == -EMSGSIZE)
                        l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
@@ -3933,12 +4395,12 @@ static int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
 
                if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr)) {
                        lm1 |= HCI_LM_ACCEPT;
-                       if (c->role_switch)
+                       if (test_bit(FLAG_ROLE_SWITCH, &c->flags))
                                lm1 |= HCI_LM_MASTER;
                        exact++;
                } else if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) {
                        lm2 |= HCI_LM_ACCEPT;
-                       if (c->role_switch)
+                       if (test_bit(FLAG_ROLE_SWITCH, &c->flags))
                                lm2 |= HCI_LM_MASTER;
                }
        }
@@ -3973,7 +4435,7 @@ static int l2cap_disconn_ind(struct hci_conn *hcon)
        BT_DBG("hcon %p", hcon);
 
        if ((hcon->type != ACL_LINK && hcon->type != LE_LINK) || !conn)
-               return 0x13;
+               return HCI_ERROR_REMOTE_USER_TERM;
 
        return conn->disc_reason;
 }
@@ -4306,3 +4768,6 @@ void l2cap_exit(void)
 
 module_param(disable_ertm, bool, 0644);
 MODULE_PARM_DESC(disable_ertm, "Disable enhanced retransmission mode");
+
+module_param(enable_hs, bool, 0644);
+MODULE_PARM_DESC(enable_hs, "Enable High Speed");
index e8292369cdcf68981b4a59969b2c4af41b703f90..567b585d9805f2232114bc8ff2c49301e53890e1 100644 (file)
@@ -333,7 +333,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us
                opts.mode     = chan->mode;
                opts.fcs      = chan->fcs;
                opts.max_tx   = chan->max_tx;
-               opts.txwin_size = (__u16)chan->tx_win;
+               opts.txwin_size = chan->tx_win;
 
                len = min_t(unsigned int, len, sizeof(opts));
                if (copy_to_user(optval, (char *) &opts, len))
@@ -358,10 +358,10 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us
                        break;
                }
 
-               if (chan->role_switch)
+               if (test_bit(FLAG_ROLE_SWITCH, &chan->flags))
                        opt |= L2CAP_LM_MASTER;
 
-               if (chan->force_reliable)
+               if (test_bit(FLAG_FORCE_RELIABLE, &chan->flags))
                        opt |= L2CAP_LM_RELIABLE;
 
                if (put_user(opt, (u32 __user *) optval))
@@ -448,7 +448,8 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch
                break;
 
        case BT_FLUSHABLE:
-               if (put_user(chan->flushable, (u32 __user *) optval))
+               if (put_user(test_bit(FLAG_FLUSHABLE, &chan->flags),
+                                               (u32 __user *) optval))
                        err = -EFAULT;
 
                break;
@@ -460,7 +461,7 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch
                        break;
                }
 
-               pwr.force_active = chan->force_active;
+               pwr.force_active = test_bit(FLAG_FORCE_ACTIVE, &chan->flags);
 
                len = min_t(unsigned int, len, sizeof(pwr));
                if (copy_to_user(optval, (char *) &pwr, len))
@@ -468,6 +469,16 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch
 
                break;
 
+       case BT_CHANNEL_POLICY:
+               if (!enable_hs) {
+                       err = -ENOPROTOOPT;
+                       break;
+               }
+
+               if (put_user(chan->chan_policy, (u32 __user *) optval))
+                       err = -EFAULT;
+               break;
+
        default:
                err = -ENOPROTOOPT;
                break;
@@ -502,7 +513,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us
                opts.mode     = chan->mode;
                opts.fcs      = chan->fcs;
                opts.max_tx   = chan->max_tx;
-               opts.txwin_size = (__u16)chan->tx_win;
+               opts.txwin_size = chan->tx_win;
 
                len = min_t(unsigned int, sizeof(opts), optlen);
                if (copy_from_user((char *) &opts, optval, len)) {
@@ -510,7 +521,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us
                        break;
                }
 
-               if (opts.txwin_size > L2CAP_DEFAULT_TX_WINDOW) {
+               if (opts.txwin_size > L2CAP_DEFAULT_EXT_WINDOW) {
                        err = -EINVAL;
                        break;
                }
@@ -534,7 +545,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us
                chan->omtu = opts.omtu;
                chan->fcs  = opts.fcs;
                chan->max_tx = opts.max_tx;
-               chan->tx_win = (__u8)opts.txwin_size;
+               chan->tx_win = opts.txwin_size;
                break;
 
        case L2CAP_LM:
@@ -550,8 +561,15 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us
                if (opt & L2CAP_LM_SECURE)
                        chan->sec_level = BT_SECURITY_HIGH;
 
-               chan->role_switch    = (opt & L2CAP_LM_MASTER);
-               chan->force_reliable = (opt & L2CAP_LM_RELIABLE);
+               if (opt & L2CAP_LM_MASTER)
+                       set_bit(FLAG_ROLE_SWITCH, &chan->flags);
+               else
+                       clear_bit(FLAG_ROLE_SWITCH, &chan->flags);
+
+               if (opt & L2CAP_LM_RELIABLE)
+                       set_bit(FLAG_FORCE_RELIABLE, &chan->flags);
+               else
+                       clear_bit(FLAG_FORCE_RELIABLE, &chan->flags);
                break;
 
        default:
@@ -657,7 +675,10 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
                        }
                }
 
-               chan->flushable = opt;
+               if (opt)
+                       set_bit(FLAG_FLUSHABLE, &chan->flags);
+               else
+                       clear_bit(FLAG_FLUSHABLE, &chan->flags);
                break;
 
        case BT_POWER:
@@ -674,7 +695,36 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
                        err = -EFAULT;
                        break;
                }
-               chan->force_active = pwr.force_active;
+
+               if (pwr.force_active)
+                       set_bit(FLAG_FORCE_ACTIVE, &chan->flags);
+               else
+                       clear_bit(FLAG_FORCE_ACTIVE, &chan->flags);
+               break;
+
+       case BT_CHANNEL_POLICY:
+               if (!enable_hs) {
+                       err = -ENOPROTOOPT;
+                       break;
+               }
+
+               if (get_user(opt, (u32 __user *) optval)) {
+                       err = -EFAULT;
+                       break;
+               }
+
+               if (opt > BT_CHANNEL_POLICY_AMP_PREFERRED) {
+                       err = -EINVAL;
+                       break;
+               }
+
+               if (chan->mode != L2CAP_MODE_ERTM &&
+                               chan->mode != L2CAP_MODE_STREAMING) {
+                       err = -EOPNOTSUPP;
+                       break;
+               }
+
+               chan->chan_policy = (u8) opt;
                break;
 
        default:
@@ -708,7 +758,7 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms
                return -ENOTCONN;
        }
 
-       err = l2cap_chan_send(chan, msg, len);
+       err = l2cap_chan_send(chan, msg, len, sk->sk_priority);
 
        release_sock(sk);
        return err;
@@ -930,11 +980,9 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
                chan->fcs  = pchan->fcs;
                chan->max_tx = pchan->max_tx;
                chan->tx_win = pchan->tx_win;
+               chan->tx_win_max = pchan->tx_win_max;
                chan->sec_level = pchan->sec_level;
-               chan->role_switch = pchan->role_switch;
-               chan->force_reliable = pchan->force_reliable;
-               chan->flushable = pchan->flushable;
-               chan->force_active = pchan->force_active;
+               chan->flags = pchan->flags;
 
                security_sk_clone(parent, sk);
        } else {
@@ -963,12 +1011,10 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
                chan->max_tx = L2CAP_DEFAULT_MAX_TX;
                chan->fcs  = L2CAP_FCS_CRC16;
                chan->tx_win = L2CAP_DEFAULT_TX_WINDOW;
+               chan->tx_win_max = L2CAP_DEFAULT_TX_WINDOW;
                chan->sec_level = BT_SECURITY_LOW;
-               chan->role_switch = 0;
-               chan->force_reliable = 0;
-               chan->flushable = BT_FLUSHABLE_OFF;
-               chan->force_active = BT_POWER_FORCE_ACTIVE_ON;
-
+               chan->flags = 0;
+               set_bit(FLAG_FORCE_ACTIVE, &chan->flags);
        }
 
        /* Default config options */
index 5caff4d47596319f41f3aebb922e43d62065ed07..a6720c6a4d2c65d1312f31f433e433a68d335a94 100644 (file)
 #define MGMT_VERSION   0
 #define MGMT_REVISION  1
 
+#define INQUIRY_LEN_BREDR 0x08 /* TGAP(100) */
+
 struct pending_cmd {
        struct list_head list;
-       __u16 opcode;
+       u16 opcode;
        int index;
        void *param;
        struct sock *sk;
        void *user_data;
 };
 
-static LIST_HEAD(cmd_list);
-
 static int cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status)
 {
        struct sk_buff *skb;
        struct mgmt_hdr *hdr;
        struct mgmt_ev_cmd_status *ev;
+       int err;
 
        BT_DBG("sock %p, index %u, cmd %u, status %u", sk, index, cmd, status);
 
@@ -65,10 +66,11 @@ static int cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status)
        ev->status = status;
        put_unaligned_le16(cmd, &ev->opcode);
 
-       if (sock_queue_rcv_skb(sk, skb) < 0)
+       err = sock_queue_rcv_skb(sk, skb);
+       if (err < 0)
                kfree_skb(skb);
 
-       return 0;
+       return err;
 }
 
 static int cmd_complete(struct sock *sk, u16 index, u16 cmd, void *rp,
@@ -77,6 +79,7 @@ static int cmd_complete(struct sock *sk, u16 index, u16 cmd, void *rp,
        struct sk_buff *skb;
        struct mgmt_hdr *hdr;
        struct mgmt_ev_cmd_complete *ev;
+       int err;
 
        BT_DBG("sock %p", sk);
 
@@ -96,10 +99,11 @@ static int cmd_complete(struct sock *sk, u16 index, u16 cmd, void *rp,
        if (rp)
                memcpy(ev->data, rp, rp_len);
 
-       if (sock_queue_rcv_skb(sk, skb) < 0)
+       err = sock_queue_rcv_skb(sk, skb);
+       if (err < 0)
                kfree_skb(skb);
 
-       return 0;
+       return err;;
 }
 
 static int read_version(struct sock *sk)
@@ -119,6 +123,7 @@ static int read_index_list(struct sock *sk)
 {
        struct mgmt_rp_read_index_list *rp;
        struct list_head *p;
+       struct hci_dev *d;
        size_t rp_len;
        u16 count;
        int i, err;
@@ -142,10 +147,9 @@ static int read_index_list(struct sock *sk)
        put_unaligned_le16(count, &rp->num_controllers);
 
        i = 0;
-       list_for_each(p, &hci_dev_list) {
-               struct hci_dev *d = list_entry(p, struct hci_dev, list);
-
-               hci_del_off_timer(d);
+       list_for_each_entry(d, &hci_dev_list, list) {
+               if (test_and_clear_bit(HCI_AUTO_OFF, &d->flags))
+                       cancel_delayed_work(&d->power_off);
 
                if (test_bit(HCI_SETUP, &d->flags))
                        continue;
@@ -175,7 +179,8 @@ static int read_controller_info(struct sock *sk, u16 index)
        if (!hdev)
                return cmd_status(sk, index, MGMT_OP_READ_INFO, ENODEV);
 
-       hci_del_off_timer(hdev);
+       if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->flags))
+               cancel_delayed_work_sync(&hdev->power_off);
 
        hci_dev_lock_bh(hdev);
 
@@ -220,7 +225,8 @@ static void mgmt_pending_free(struct pending_cmd *cmd)
 }
 
 static struct pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode,
-                                               u16 index, void *data, u16 len)
+                                                       struct hci_dev *hdev,
+                                                       void *data, u16 len)
 {
        struct pending_cmd *cmd;
 
@@ -229,7 +235,7 @@ static struct pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode,
                return NULL;
 
        cmd->opcode = opcode;
-       cmd->index = index;
+       cmd->index = hdev->id;
 
        cmd->param = kmalloc(len, GFP_ATOMIC);
        if (!cmd->param) {
@@ -243,48 +249,36 @@ static struct pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode,
        cmd->sk = sk;
        sock_hold(sk);
 
-       list_add(&cmd->list, &cmd_list);
+       list_add(&cmd->list, &hdev->mgmt_pending);
 
        return cmd;
 }
 
-static void mgmt_pending_foreach(u16 opcode, int index,
+static void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev,
                                void (*cb)(struct pending_cmd *cmd, void *data),
                                void *data)
 {
        struct list_head *p, *n;
 
-       list_for_each_safe(p, n, &cmd_list) {
+       list_for_each_safe(p, n, &hdev->mgmt_pending) {
                struct pending_cmd *cmd;
 
                cmd = list_entry(p, struct pending_cmd, list);
 
-               if (cmd->opcode != opcode)
-                       continue;
-
-               if (index >= 0 && cmd->index != index)
+               if (opcode > 0 && cmd->opcode != opcode)
                        continue;
 
                cb(cmd, data);
        }
 }
 
-static struct pending_cmd *mgmt_pending_find(u16 opcode, int index)
+static struct pending_cmd *mgmt_pending_find(u16 opcode, struct hci_dev *hdev)
 {
-       struct list_head *p;
-
-       list_for_each(p, &cmd_list) {
-               struct pending_cmd *cmd;
-
-               cmd = list_entry(p, struct pending_cmd, list);
-
-               if (cmd->opcode != opcode)
-                       continue;
-
-               if (index >= 0 && cmd->index != index)
-                       continue;
+       struct pending_cmd *cmd;
 
-               return cmd;
+       list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
+               if (cmd->opcode == opcode)
+                       return cmd;
        }
 
        return NULL;
@@ -322,12 +316,12 @@ static int set_powered(struct sock *sk, u16 index, unsigned char *data, u16 len)
                goto failed;
        }
 
-       if (mgmt_pending_find(MGMT_OP_SET_POWERED, index)) {
+       if (mgmt_pending_find(MGMT_OP_SET_POWERED, hdev)) {
                err = cmd_status(sk, index, MGMT_OP_SET_POWERED, EBUSY);
                goto failed;
        }
 
-       cmd = mgmt_pending_add(sk, MGMT_OP_SET_POWERED, index, data, len);
+       cmd = mgmt_pending_add(sk, MGMT_OP_SET_POWERED, hdev, data, len);
        if (!cmd) {
                err = -ENOMEM;
                goto failed;
@@ -336,7 +330,7 @@ static int set_powered(struct sock *sk, u16 index, unsigned char *data, u16 len)
        if (cp->val)
                queue_work(hdev->workqueue, &hdev->power_on);
        else
-               queue_work(hdev->workqueue, &hdev->power_off);
+               queue_work(hdev->workqueue, &hdev->power_off.work);
 
        err = 0;
 
@@ -349,7 +343,7 @@ failed:
 static int set_discoverable(struct sock *sk, u16 index, unsigned char *data,
                                                                        u16 len)
 {
-       struct mgmt_mode *cp;
+       struct mgmt_cp_set_discoverable *cp;
        struct hci_dev *hdev;
        struct pending_cmd *cmd;
        u8 scan;
@@ -373,8 +367,8 @@ static int set_discoverable(struct sock *sk, u16 index, unsigned char *data,
                goto failed;
        }
 
-       if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, index) ||
-                       mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, index)) {
+       if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) ||
+                       mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) {
                err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, EBUSY);
                goto failed;
        }
@@ -385,7 +379,7 @@ static int set_discoverable(struct sock *sk, u16 index, unsigned char *data,
                goto failed;
        }
 
-       cmd = mgmt_pending_add(sk, MGMT_OP_SET_DISCOVERABLE, index, data, len);
+       cmd = mgmt_pending_add(sk, MGMT_OP_SET_DISCOVERABLE, hdev, data, len);
        if (!cmd) {
                err = -ENOMEM;
                goto failed;
@@ -395,11 +389,16 @@ static int set_discoverable(struct sock *sk, u16 index, unsigned char *data,
 
        if (cp->val)
                scan |= SCAN_INQUIRY;
+       else
+               cancel_delayed_work(&hdev->discov_off);
 
        err = hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
        if (err < 0)
                mgmt_pending_remove(cmd);
 
+       if (cp->val)
+               hdev->discov_timeout = get_unaligned_le16(&cp->timeout);
+
 failed:
        hci_dev_unlock_bh(hdev);
        hci_dev_put(hdev);
@@ -434,8 +433,8 @@ static int set_connectable(struct sock *sk, u16 index, unsigned char *data,
                goto failed;
        }
 
-       if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, index) ||
-                       mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, index)) {
+       if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) ||
+                       mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) {
                err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, EBUSY);
                goto failed;
        }
@@ -445,7 +444,7 @@ static int set_connectable(struct sock *sk, u16 index, unsigned char *data,
                goto failed;
        }
 
-       cmd = mgmt_pending_add(sk, MGMT_OP_SET_CONNECTABLE, index, data, len);
+       cmd = mgmt_pending_add(sk, MGMT_OP_SET_CONNECTABLE, hdev, data, len);
        if (!cmd) {
                err = -ENOMEM;
                goto failed;
@@ -467,8 +466,8 @@ failed:
        return err;
 }
 
-static int mgmt_event(u16 event, u16 index, void *data, u16 data_len,
-                                                       struct sock *skip_sk)
+static int mgmt_event(u16 event, struct hci_dev *hdev, void *data,
+                                       u16 data_len, struct sock *skip_sk)
 {
        struct sk_buff *skb;
        struct mgmt_hdr *hdr;
@@ -481,7 +480,10 @@ static int mgmt_event(u16 event, u16 index, void *data, u16 data_len,
 
        hdr = (void *) skb_put(skb, sizeof(*hdr));
        hdr->opcode = cpu_to_le16(event);
-       hdr->index = cpu_to_le16(index);
+       if (hdev)
+               hdr->index = cpu_to_le16(hdev->id);
+       else
+               hdr->index = cpu_to_le16(MGMT_INDEX_NONE);
        hdr->len = cpu_to_le16(data_len);
 
        if (data)
@@ -533,7 +535,7 @@ static int set_pairable(struct sock *sk, u16 index, unsigned char *data,
 
        ev.val = cp->val;
 
-       err = mgmt_event(MGMT_EV_PAIRABLE, index, &ev, sizeof(ev), sk);
+       err = mgmt_event(MGMT_EV_PAIRABLE, hdev, &ev, sizeof(ev), sk);
 
 failed:
        hci_dev_unlock_bh(hdev);
@@ -586,7 +588,7 @@ static void create_eir(struct hci_dev *hdev, u8 *data)
        u16 eir_len = 0;
        u16 uuid16_list[HCI_MAX_EIR_LENGTH / sizeof(u16)];
        int i, truncated = 0;
-       struct list_head *p;
+       struct bt_uuid *uuid;
        size_t name_len;
 
        name_len = strlen(hdev->dev_name);
@@ -611,8 +613,7 @@ static void create_eir(struct hci_dev *hdev, u8 *data)
        memset(uuid16_list, 0, sizeof(uuid16_list));
 
        /* Group all UUID16 types */
-       list_for_each(p, &hdev->uuids) {
-               struct bt_uuid *uuid = list_entry(p, struct bt_uuid, list);
+       list_for_each_entry(uuid, &hdev->uuids, list) {
                u16 uuid16;
 
                uuid16 = get_uuid16(uuid->uuid);
@@ -688,14 +689,11 @@ static int update_eir(struct hci_dev *hdev)
 
 static u8 get_service_classes(struct hci_dev *hdev)
 {
-       struct list_head *p;
+       struct bt_uuid *uuid;
        u8 val = 0;
 
-       list_for_each(p, &hdev->uuids) {
-               struct bt_uuid *uuid = list_entry(p, struct bt_uuid, list);
-
+       list_for_each_entry(uuid, &hdev->uuids, list)
                val |= uuid->svc_hint;
-       }
 
        return val;
 }
@@ -894,6 +892,9 @@ static int set_service_cache(struct sock *sk, u16 index,  unsigned char *data,
        if (err == 0)
                err = cmd_complete(sk, index, MGMT_OP_SET_SERVICE_CACHE, NULL,
                                                                        0);
+       else
+               cmd_status(sk, index, MGMT_OP_SET_SERVICE_CACHE, -err);
+
 
        hci_dev_unlock_bh(hdev);
        hci_dev_put(hdev);
@@ -901,30 +902,32 @@ static int set_service_cache(struct sock *sk, u16 index,  unsigned char *data,
        return err;
 }
 
-static int load_keys(struct sock *sk, u16 index, unsigned char *data, u16 len)
+static int load_link_keys(struct sock *sk, u16 index, unsigned char *data,
+                                                               u16 len)
 {
        struct hci_dev *hdev;
-       struct mgmt_cp_load_keys *cp;
+       struct mgmt_cp_load_link_keys *cp;
        u16 key_count, expected_len;
        int i;
 
        cp = (void *) data;
 
        if (len < sizeof(*cp))
-               return -EINVAL;
+               return cmd_status(sk, index, MGMT_OP_LOAD_LINK_KEYS, EINVAL);
 
        key_count = get_unaligned_le16(&cp->key_count);
 
-       expected_len = sizeof(*cp) + key_count * sizeof(struct mgmt_key_info);
+       expected_len = sizeof(*cp) + key_count *
+                                       sizeof(struct mgmt_link_key_info);
        if (expected_len != len) {
-               BT_ERR("load_keys: expected %u bytes, got %u bytes",
+               BT_ERR("load_link_keys: expected %u bytes, got %u bytes",
                                                        len, expected_len);
-               return -EINVAL;
+               return cmd_status(sk, index, MGMT_OP_LOAD_LINK_KEYS, EINVAL);
        }
 
        hdev = hci_dev_get(index);
        if (!hdev)
-               return cmd_status(sk, index, MGMT_OP_LOAD_KEYS, ENODEV);
+               return cmd_status(sk, index, MGMT_OP_LOAD_LINK_KEYS, ENODEV);
 
        BT_DBG("hci%u debug_keys %u key_count %u", index, cp->debug_keys,
                                                                key_count);
@@ -941,7 +944,7 @@ static int load_keys(struct sock *sk, u16 index, unsigned char *data, u16 len)
                clear_bit(HCI_DEBUG_KEYS, &hdev->flags);
 
        for (i = 0; i < key_count; i++) {
-               struct mgmt_key_info *key = &cp->keys[i];
+               struct mgmt_link_key_info *key = &cp->keys[i];
 
                hci_add_link_key(hdev, NULL, 0, &key->bdaddr, key->val, key->type,
                                                                key->pin_len);
@@ -953,27 +956,28 @@ static int load_keys(struct sock *sk, u16 index, unsigned char *data, u16 len)
        return 0;
 }
 
-static int remove_key(struct sock *sk, u16 index, unsigned char *data, u16 len)
+static int remove_keys(struct sock *sk, u16 index, unsigned char *data,
+                                                               u16 len)
 {
        struct hci_dev *hdev;
-       struct mgmt_cp_remove_key *cp;
+       struct mgmt_cp_remove_keys *cp;
        struct hci_conn *conn;
        int err;
 
        cp = (void *) data;
 
        if (len != sizeof(*cp))
-               return cmd_status(sk, index, MGMT_OP_REMOVE_KEY, EINVAL);
+               return cmd_status(sk, index, MGMT_OP_REMOVE_KEYS, EINVAL);
 
        hdev = hci_dev_get(index);
        if (!hdev)
-               return cmd_status(sk, index, MGMT_OP_REMOVE_KEY, ENODEV);
+               return cmd_status(sk, index, MGMT_OP_REMOVE_KEYS, ENODEV);
 
        hci_dev_lock_bh(hdev);
 
        err = hci_remove_link_key(hdev, &cp->bdaddr);
        if (err < 0) {
-               err = cmd_status(sk, index, MGMT_OP_REMOVE_KEY, -err);
+               err = cmd_status(sk, index, MGMT_OP_REMOVE_KEYS, -err);
                goto unlock;
        }
 
@@ -1025,7 +1029,7 @@ static int disconnect(struct sock *sk, u16 index, unsigned char *data, u16 len)
                goto failed;
        }
 
-       if (mgmt_pending_find(MGMT_OP_DISCONNECT, index)) {
+       if (mgmt_pending_find(MGMT_OP_DISCONNECT, hdev)) {
                err = cmd_status(sk, index, MGMT_OP_DISCONNECT, EBUSY);
                goto failed;
        }
@@ -1039,7 +1043,7 @@ static int disconnect(struct sock *sk, u16 index, unsigned char *data, u16 len)
                goto failed;
        }
 
-       cmd = mgmt_pending_add(sk, MGMT_OP_DISCONNECT, index, data, len);
+       cmd = mgmt_pending_add(sk, MGMT_OP_DISCONNECT, hdev, data, len);
        if (!cmd) {
                err = -ENOMEM;
                goto failed;
@@ -1059,10 +1063,23 @@ failed:
        return err;
 }
 
+static u8 link_to_mgmt(u8 link_type)
+{
+       switch (link_type) {
+       case LE_LINK:
+               return MGMT_ADDR_LE;
+       case ACL_LINK:
+               return MGMT_ADDR_BREDR;
+       default:
+               return MGMT_ADDR_INVALID;
+       }
+}
+
 static int get_connections(struct sock *sk, u16 index)
 {
        struct mgmt_rp_get_connections *rp;
        struct hci_dev *hdev;
+       struct hci_conn *c;
        struct list_head *p;
        size_t rp_len;
        u16 count;
@@ -1081,7 +1098,7 @@ static int get_connections(struct sock *sk, u16 index)
                count++;
        }
 
-       rp_len = sizeof(*rp) + (count * sizeof(bdaddr_t));
+       rp_len = sizeof(*rp) + (count * sizeof(struct mgmt_addr_info));
        rp = kmalloc(rp_len, GFP_ATOMIC);
        if (!rp) {
                err = -ENOMEM;
@@ -1091,12 +1108,17 @@ static int get_connections(struct sock *sk, u16 index)
        put_unaligned_le16(count, &rp->conn_count);
 
        i = 0;
-       list_for_each(p, &hdev->conn_hash.list) {
-               struct hci_conn *c = list_entry(p, struct hci_conn, list);
-
-               bacpy(&rp->conn[i++], &c->dst);
+       list_for_each_entry(c, &hdev->conn_hash.list, list) {
+               bacpy(&rp->addr[i].bdaddr, &c->dst);
+               rp->addr[i].type = link_to_mgmt(c->type);
+               if (rp->addr[i].type == MGMT_ADDR_INVALID)
+                       continue;
+               i++;
        }
 
+       /* Recalculate length in case of filtered SCO connections, etc */
+       rp_len = sizeof(*rp) + (i * sizeof(struct mgmt_addr_info));
+
        err = cmd_complete(sk, index, MGMT_OP_GET_CONNECTIONS, rp, rp_len);
 
 unlock:
@@ -1112,7 +1134,7 @@ static int send_pin_code_neg_reply(struct sock *sk, u16 index,
        struct pending_cmd *cmd;
        int err;
 
-       cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_NEG_REPLY, index, cp,
+       cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_NEG_REPLY, hdev, cp,
                                                                sizeof(*cp));
        if (!cmd)
                return -ENOMEM;
@@ -1173,7 +1195,7 @@ static int pin_code_reply(struct sock *sk, u16 index, unsigned char *data,
                goto failed;
        }
 
-       cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_REPLY, index, data, len);
+       cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_REPLY, hdev, data, len);
        if (!cmd) {
                err = -ENOMEM;
                goto failed;
@@ -1264,19 +1286,12 @@ static int set_io_capability(struct sock *sk, u16 index, unsigned char *data,
 static inline struct pending_cmd *find_pairing(struct hci_conn *conn)
 {
        struct hci_dev *hdev = conn->hdev;
-       struct list_head *p;
-
-       list_for_each(p, &cmd_list) {
-               struct pending_cmd *cmd;
-
-               cmd = list_entry(p, struct pending_cmd, list);
+       struct pending_cmd *cmd;
 
+       list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
                if (cmd->opcode != MGMT_OP_PAIR_DEVICE)
                        continue;
 
-               if (cmd->index != hdev->id)
-                       continue;
-
                if (cmd->user_data != conn)
                        continue;
 
@@ -1309,16 +1324,19 @@ static void pairing_complete(struct pending_cmd *cmd, u8 status)
 static void pairing_complete_cb(struct hci_conn *conn, u8 status)
 {
        struct pending_cmd *cmd;
+       struct hci_dev *hdev = conn->hdev;
 
        BT_DBG("status %u", status);
 
+       hci_dev_lock_bh(hdev);
+
        cmd = find_pairing(conn);
-       if (!cmd) {
+       if (!cmd)
                BT_DBG("Unable to find a pending command");
-               return;
-       }
+       else
+               pairing_complete(cmd, status);
 
-       pairing_complete(cmd, status);
+       hci_dev_unlock_bh(hdev);
 }
 
 static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len)
@@ -1369,7 +1387,7 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len)
                goto unlock;
        }
 
-       cmd = mgmt_pending_add(sk, MGMT_OP_PAIR_DEVICE, index, data, len);
+       cmd = mgmt_pending_add(sk, MGMT_OP_PAIR_DEVICE, hdev, data, len);
        if (!cmd) {
                err = -ENOMEM;
                hci_conn_put(conn);
@@ -1431,7 +1449,7 @@ static int user_confirm_reply(struct sock *sk, u16 index, unsigned char *data,
                goto failed;
        }
 
-       cmd = mgmt_pending_add(sk, mgmt_op, index, data, len);
+       cmd = mgmt_pending_add(sk, mgmt_op, hdev, data, len);
        if (!cmd) {
                err = -ENOMEM;
                goto failed;
@@ -1468,7 +1486,7 @@ static int set_local_name(struct sock *sk, u16 index, unsigned char *data,
 
        hci_dev_lock_bh(hdev);
 
-       cmd = mgmt_pending_add(sk, MGMT_OP_SET_LOCAL_NAME, index, data, len);
+       cmd = mgmt_pending_add(sk, MGMT_OP_SET_LOCAL_NAME, hdev, data, len);
        if (!cmd) {
                err = -ENOMEM;
                goto failed;
@@ -1514,12 +1532,12 @@ static int read_local_oob_data(struct sock *sk, u16 index)
                goto unlock;
        }
 
-       if (mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, index)) {
+       if (mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev)) {
                err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA, EBUSY);
                goto unlock;
        }
 
-       cmd = mgmt_pending_add(sk, MGMT_OP_READ_LOCAL_OOB_DATA, index, NULL, 0);
+       cmd = mgmt_pending_add(sk, MGMT_OP_READ_LOCAL_OOB_DATA, hdev, NULL, 0);
        if (!cmd) {
                err = -ENOMEM;
                goto unlock;
@@ -1606,8 +1624,6 @@ static int remove_remote_oob_data(struct sock *sk, u16 index,
 
 static int start_discovery(struct sock *sk, u16 index)
 {
-       u8 lap[3] = { 0x33, 0x8b, 0x9e };
-       struct hci_cp_inquiry cp;
        struct pending_cmd *cmd;
        struct hci_dev *hdev;
        int err;
@@ -1620,18 +1636,18 @@ static int start_discovery(struct sock *sk, u16 index)
 
        hci_dev_lock_bh(hdev);
 
-       cmd = mgmt_pending_add(sk, MGMT_OP_START_DISCOVERY, index, NULL, 0);
+       if (!test_bit(HCI_UP, &hdev->flags)) {
+               err = cmd_status(sk, index, MGMT_OP_START_DISCOVERY, ENETDOWN);
+               goto failed;
+       }
+
+       cmd = mgmt_pending_add(sk, MGMT_OP_START_DISCOVERY, hdev, NULL, 0);
        if (!cmd) {
                err = -ENOMEM;
                goto failed;
        }
 
-       memset(&cp, 0, sizeof(cp));
-       memcpy(&cp.lap, lap, 3);
-       cp.length  = 0x08;
-       cp.num_rsp = 0x00;
-
-       err = hci_send_cmd(hdev, HCI_OP_INQUIRY, sizeof(cp), &cp);
+       err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR);
        if (err < 0)
                mgmt_pending_remove(cmd);
 
@@ -1656,13 +1672,13 @@ static int stop_discovery(struct sock *sk, u16 index)
 
        hci_dev_lock_bh(hdev);
 
-       cmd = mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, index, NULL, 0);
+       cmd = mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, hdev, NULL, 0);
        if (!cmd) {
                err = -ENOMEM;
                goto failed;
        }
 
-       err = hci_send_cmd(hdev, HCI_OP_INQUIRY_CANCEL, 0, NULL);
+       err = hci_cancel_inquiry(hdev);
        if (err < 0)
                mgmt_pending_remove(cmd);
 
@@ -1677,7 +1693,6 @@ static int block_device(struct sock *sk, u16 index, unsigned char *data,
                                                                u16 len)
 {
        struct hci_dev *hdev;
-       struct pending_cmd *cmd;
        struct mgmt_cp_block_device *cp = (void *) data;
        int err;
 
@@ -1694,23 +1709,13 @@ static int block_device(struct sock *sk, u16 index, unsigned char *data,
 
        hci_dev_lock_bh(hdev);
 
-       cmd = mgmt_pending_add(sk, MGMT_OP_BLOCK_DEVICE, index, NULL, 0);
-       if (!cmd) {
-               err = -ENOMEM;
-               goto failed;
-       }
-
        err = hci_blacklist_add(hdev, &cp->bdaddr);
-
        if (err < 0)
                err = cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE, -err);
        else
                err = cmd_complete(sk, index, MGMT_OP_BLOCK_DEVICE,
                                                        NULL, 0);
 
-       mgmt_pending_remove(cmd);
-
-failed:
        hci_dev_unlock_bh(hdev);
        hci_dev_put(hdev);
 
@@ -1721,7 +1726,6 @@ static int unblock_device(struct sock *sk, u16 index, unsigned char *data,
                                                                u16 len)
 {
        struct hci_dev *hdev;
-       struct pending_cmd *cmd;
        struct mgmt_cp_unblock_device *cp = (void *) data;
        int err;
 
@@ -1738,12 +1742,6 @@ static int unblock_device(struct sock *sk, u16 index, unsigned char *data,
 
        hci_dev_lock_bh(hdev);
 
-       cmd = mgmt_pending_add(sk, MGMT_OP_UNBLOCK_DEVICE, index, NULL, 0);
-       if (!cmd) {
-               err = -ENOMEM;
-               goto failed;
-       }
-
        err = hci_blacklist_del(hdev, &cp->bdaddr);
 
        if (err < 0)
@@ -1752,9 +1750,6 @@ static int unblock_device(struct sock *sk, u16 index, unsigned char *data,
                err = cmd_complete(sk, index, MGMT_OP_UNBLOCK_DEVICE,
                                                                NULL, 0);
 
-       mgmt_pending_remove(cmd);
-
-failed:
        hci_dev_unlock_bh(hdev);
        hci_dev_put(hdev);
 
@@ -1882,11 +1877,11 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
        case MGMT_OP_SET_SERVICE_CACHE:
                err = set_service_cache(sk, index, buf + sizeof(*hdr), len);
                break;
-       case MGMT_OP_LOAD_KEYS:
-               err = load_keys(sk, index, buf + sizeof(*hdr), len);
+       case MGMT_OP_LOAD_LINK_KEYS:
+               err = load_link_keys(sk, index, buf + sizeof(*hdr), len);
                break;
-       case MGMT_OP_REMOVE_KEY:
-               err = remove_key(sk, index, buf + sizeof(*hdr), len);
+       case MGMT_OP_REMOVE_KEYS:
+               err = remove_keys(sk, index, buf + sizeof(*hdr), len);
                break;
        case MGMT_OP_DISCONNECT:
                err = disconnect(sk, index, buf + sizeof(*hdr), len);
@@ -1957,14 +1952,26 @@ done:
        return err;
 }
 
-int mgmt_index_added(u16 index)
+static void cmd_status_rsp(struct pending_cmd *cmd, void *data)
 {
-       return mgmt_event(MGMT_EV_INDEX_ADDED, index, NULL, 0, NULL);
+       u8 *status = data;
+
+       cmd_status(cmd->sk, cmd->index, cmd->opcode, *status);
+       mgmt_pending_remove(cmd);
 }
 
-int mgmt_index_removed(u16 index)
+int mgmt_index_added(struct hci_dev *hdev)
 {
-       return mgmt_event(MGMT_EV_INDEX_REMOVED, index, NULL, 0, NULL);
+       return mgmt_event(MGMT_EV_INDEX_ADDED, hdev, NULL, 0, NULL);
+}
+
+int mgmt_index_removed(struct hci_dev *hdev)
+{
+       u8 status = ENODEV;
+
+       mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status);
+
+       return mgmt_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, NULL);
 }
 
 struct cmd_lookup {
@@ -1992,17 +1999,22 @@ static void mode_rsp(struct pending_cmd *cmd, void *data)
        mgmt_pending_free(cmd);
 }
 
-int mgmt_powered(u16 index, u8 powered)
+int mgmt_powered(struct hci_dev *hdev, u8 powered)
 {
        struct mgmt_mode ev;
        struct cmd_lookup match = { powered, NULL };
        int ret;
 
-       mgmt_pending_foreach(MGMT_OP_SET_POWERED, index, mode_rsp, &match);
+       mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, mode_rsp, &match);
+
+       if (!powered) {
+               u8 status = ENETDOWN;
+               mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status);
+       }
 
        ev.val = powered;
 
-       ret = mgmt_event(MGMT_EV_POWERED, index, &ev, sizeof(ev), match.sk);
+       ret = mgmt_event(MGMT_EV_POWERED, hdev, &ev, sizeof(ev), match.sk);
 
        if (match.sk)
                sock_put(match.sk);
@@ -2010,17 +2022,17 @@ int mgmt_powered(u16 index, u8 powered)
        return ret;
 }
 
-int mgmt_discoverable(u16 index, u8 discoverable)
+int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable)
 {
        struct mgmt_mode ev;
        struct cmd_lookup match = { discoverable, NULL };
        int ret;
 
-       mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, index, mode_rsp, &match);
+       mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev, mode_rsp, &match);
 
        ev.val = discoverable;
 
-       ret = mgmt_event(MGMT_EV_DISCOVERABLE, index, &ev, sizeof(ev),
+       ret = mgmt_event(MGMT_EV_DISCOVERABLE, hdev, &ev, sizeof(ev),
                                                                match.sk);
 
        if (match.sk)
@@ -2029,17 +2041,17 @@ int mgmt_discoverable(u16 index, u8 discoverable)
        return ret;
 }
 
-int mgmt_connectable(u16 index, u8 connectable)
+int mgmt_connectable(struct hci_dev *hdev, u8 connectable)
 {
        struct mgmt_mode ev;
        struct cmd_lookup match = { connectable, NULL };
        int ret;
 
-       mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, index, mode_rsp, &match);
+       mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev, mode_rsp, &match);
 
        ev.val = connectable;
 
-       ret = mgmt_event(MGMT_EV_CONNECTABLE, index, &ev, sizeof(ev), match.sk);
+       ret = mgmt_event(MGMT_EV_CONNECTABLE, hdev, &ev, sizeof(ev), match.sk);
 
        if (match.sk)
                sock_put(match.sk);
@@ -2047,9 +2059,23 @@ int mgmt_connectable(u16 index, u8 connectable)
        return ret;
 }
 
-int mgmt_new_key(u16 index, struct link_key *key, u8 persistent)
+int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status)
+{
+       if (scan & SCAN_PAGE)
+               mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev,
+                                               cmd_status_rsp, &status);
+
+       if (scan & SCAN_INQUIRY)
+               mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev,
+                                               cmd_status_rsp, &status);
+
+       return 0;
+}
+
+int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
+                                                               u8 persistent)
 {
-       struct mgmt_ev_new_key ev;
+       struct mgmt_ev_new_link_key ev;
 
        memset(&ev, 0, sizeof(ev));
 
@@ -2059,17 +2085,17 @@ int mgmt_new_key(u16 index, struct link_key *key, u8 persistent)
        memcpy(ev.key.val, key->val, 16);
        ev.key.pin_len = key->pin_len;
 
-       return mgmt_event(MGMT_EV_NEW_KEY, index, &ev, sizeof(ev), NULL);
+       return mgmt_event(MGMT_EV_NEW_LINK_KEY, hdev, &ev, sizeof(ev), NULL);
 }
 
-int mgmt_connected(u16 index, bdaddr_t *bdaddr, u8 link_type)
+int mgmt_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type)
 {
-       struct mgmt_ev_connected ev;
+       struct mgmt_addr_info ev;
 
        bacpy(&ev.bdaddr, bdaddr);
-       ev.link_type = link_type;
+       ev.type = link_to_mgmt(link_type);
 
-       return mgmt_event(MGMT_EV_CONNECTED, index, &ev, sizeof(ev), NULL);
+       return mgmt_event(MGMT_EV_CONNECTED, hdev, &ev, sizeof(ev), NULL);
 }
 
 static void disconnect_rsp(struct pending_cmd *cmd, void *data)
@@ -2088,17 +2114,18 @@ static void disconnect_rsp(struct pending_cmd *cmd, void *data)
        mgmt_pending_remove(cmd);
 }
 
-int mgmt_disconnected(u16 index, bdaddr_t *bdaddr)
+int mgmt_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
 {
-       struct mgmt_ev_disconnected ev;
+       struct mgmt_addr_info ev;
        struct sock *sk = NULL;
        int err;
 
-       mgmt_pending_foreach(MGMT_OP_DISCONNECT, index, disconnect_rsp, &sk);
+       mgmt_pending_foreach(MGMT_OP_DISCONNECT, hdev, disconnect_rsp, &sk);
 
        bacpy(&ev.bdaddr, bdaddr);
+       ev.type = link_to_mgmt(type);
 
-       err = mgmt_event(MGMT_EV_DISCONNECTED, index, &ev, sizeof(ev), sk);
+       err = mgmt_event(MGMT_EV_DISCONNECTED, hdev, &ev, sizeof(ev), sk);
 
        if (sk)
                sock_put(sk);
@@ -2106,57 +2133,60 @@ int mgmt_disconnected(u16 index, bdaddr_t *bdaddr)
        return err;
 }
 
-int mgmt_disconnect_failed(u16 index)
+int mgmt_disconnect_failed(struct hci_dev *hdev)
 {
        struct pending_cmd *cmd;
        int err;
 
-       cmd = mgmt_pending_find(MGMT_OP_DISCONNECT, index);
+       cmd = mgmt_pending_find(MGMT_OP_DISCONNECT, hdev);
        if (!cmd)
                return -ENOENT;
 
-       err = cmd_status(cmd->sk, index, MGMT_OP_DISCONNECT, EIO);
+       err = cmd_status(cmd->sk, hdev->id, MGMT_OP_DISCONNECT, EIO);
 
        mgmt_pending_remove(cmd);
 
        return err;
 }
 
-int mgmt_connect_failed(u16 index, bdaddr_t *bdaddr, u8 status)
+int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type,
+                                                               u8 status)
 {
        struct mgmt_ev_connect_failed ev;
 
-       bacpy(&ev.bdaddr, bdaddr);
+       bacpy(&ev.addr.bdaddr, bdaddr);
+       ev.addr.type = link_to_mgmt(type);
        ev.status = status;
 
-       return mgmt_event(MGMT_EV_CONNECT_FAILED, index, &ev, sizeof(ev), NULL);
+       return mgmt_event(MGMT_EV_CONNECT_FAILED, hdev, &ev, sizeof(ev), NULL);
 }
 
-int mgmt_pin_code_request(u16 index, bdaddr_t *bdaddr, u8 secure)
+int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure)
 {
        struct mgmt_ev_pin_code_request ev;
 
        bacpy(&ev.bdaddr, bdaddr);
        ev.secure = secure;
 
-       return mgmt_event(MGMT_EV_PIN_CODE_REQUEST, index, &ev, sizeof(ev),
+       return mgmt_event(MGMT_EV_PIN_CODE_REQUEST, hdev, &ev, sizeof(ev),
                                                                        NULL);
 }
 
-int mgmt_pin_code_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status)
+int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                                                               u8 status)
 {
        struct pending_cmd *cmd;
        struct mgmt_rp_pin_code_reply rp;
        int err;
 
-       cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_REPLY, index);
+       cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_REPLY, hdev);
        if (!cmd)
                return -ENOENT;
 
        bacpy(&rp.bdaddr, bdaddr);
        rp.status = status;
 
-       err = cmd_complete(cmd->sk, index, MGMT_OP_PIN_CODE_REPLY, &rp,
+       err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_REPLY, &rp,
                                                                sizeof(rp));
 
        mgmt_pending_remove(cmd);
@@ -2164,20 +2194,21 @@ int mgmt_pin_code_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status)
        return err;
 }
 
-int mgmt_pin_code_neg_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status)
+int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                                                               u8 status)
 {
        struct pending_cmd *cmd;
        struct mgmt_rp_pin_code_reply rp;
        int err;
 
-       cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_NEG_REPLY, index);
+       cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_NEG_REPLY, hdev);
        if (!cmd)
                return -ENOENT;
 
        bacpy(&rp.bdaddr, bdaddr);
        rp.status = status;
 
-       err = cmd_complete(cmd->sk, index, MGMT_OP_PIN_CODE_NEG_REPLY, &rp,
+       err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY, &rp,
                                                                sizeof(rp));
 
        mgmt_pending_remove(cmd);
@@ -2185,97 +2216,93 @@ int mgmt_pin_code_neg_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status)
        return err;
 }
 
-int mgmt_user_confirm_request(u16 index, bdaddr_t *bdaddr, __le32 value,
-                                                       u8 confirm_hint)
+int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                                               __le32 value, u8 confirm_hint)
 {
        struct mgmt_ev_user_confirm_request ev;
 
-       BT_DBG("hci%u", index);
+       BT_DBG("%s", hdev->name);
 
        bacpy(&ev.bdaddr, bdaddr);
        ev.confirm_hint = confirm_hint;
        put_unaligned_le32(value, &ev.value);
 
-       return mgmt_event(MGMT_EV_USER_CONFIRM_REQUEST, index, &ev, sizeof(ev),
+       return mgmt_event(MGMT_EV_USER_CONFIRM_REQUEST, hdev, &ev, sizeof(ev),
                                                                        NULL);
 }
 
-static int confirm_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status,
-                                                               u8 opcode)
+static int confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                                                       u8 status, u8 opcode)
 {
        struct pending_cmd *cmd;
        struct mgmt_rp_user_confirm_reply rp;
        int err;
 
-       cmd = mgmt_pending_find(opcode, index);
+       cmd = mgmt_pending_find(opcode, hdev);
        if (!cmd)
                return -ENOENT;
 
        bacpy(&rp.bdaddr, bdaddr);
        rp.status = status;
-       err = cmd_complete(cmd->sk, index, opcode, &rp, sizeof(rp));
+       err = cmd_complete(cmd->sk, hdev->id, opcode, &rp, sizeof(rp));
 
        mgmt_pending_remove(cmd);
 
        return err;
 }
 
-int mgmt_user_confirm_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status)
+int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                                                               u8 status)
 {
-       return confirm_reply_complete(index, bdaddr, status,
+       return confirm_reply_complete(hdev, bdaddr, status,
                                                MGMT_OP_USER_CONFIRM_REPLY);
 }
 
-int mgmt_user_confirm_neg_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status)
+int mgmt_user_confirm_neg_reply_complete(struct hci_dev *hdev,
+                                               bdaddr_t *bdaddr, u8 status)
 {
-       return confirm_reply_complete(index, bdaddr, status,
+       return confirm_reply_complete(hdev, bdaddr, status,
                                        MGMT_OP_USER_CONFIRM_NEG_REPLY);
 }
 
-int mgmt_auth_failed(u16 index, bdaddr_t *bdaddr, u8 status)
+int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 status)
 {
        struct mgmt_ev_auth_failed ev;
 
        bacpy(&ev.bdaddr, bdaddr);
        ev.status = status;
 
-       return mgmt_event(MGMT_EV_AUTH_FAILED, index, &ev, sizeof(ev), NULL);
+       return mgmt_event(MGMT_EV_AUTH_FAILED, hdev, &ev, sizeof(ev), NULL);
 }
 
-int mgmt_set_local_name_complete(u16 index, u8 *name, u8 status)
+int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status)
 {
        struct pending_cmd *cmd;
-       struct hci_dev *hdev;
        struct mgmt_cp_set_local_name ev;
        int err;
 
        memset(&ev, 0, sizeof(ev));
        memcpy(ev.name, name, HCI_MAX_NAME_LENGTH);
 
-       cmd = mgmt_pending_find(MGMT_OP_SET_LOCAL_NAME, index);
+       cmd = mgmt_pending_find(MGMT_OP_SET_LOCAL_NAME, hdev);
        if (!cmd)
                goto send_event;
 
        if (status) {
-               err = cmd_status(cmd->sk, index, MGMT_OP_SET_LOCAL_NAME, EIO);
+               err = cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME,
+                                                                       EIO);
                goto failed;
        }
 
-       hdev = hci_dev_get(index);
-       if (hdev) {
-               hci_dev_lock_bh(hdev);
-               update_eir(hdev);
-               hci_dev_unlock_bh(hdev);
-               hci_dev_put(hdev);
-       }
+       update_eir(hdev);
 
-       err = cmd_complete(cmd->sk, index, MGMT_OP_SET_LOCAL_NAME, &ev,
+       err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, &ev,
                                                                sizeof(ev));
        if (err < 0)
                goto failed;
 
 send_event:
-       err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, index, &ev, sizeof(ev),
+       err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, sizeof(ev),
                                                        cmd ? cmd->sk : NULL);
 
 failed:
@@ -2284,29 +2311,30 @@ failed:
        return err;
 }
 
-int mgmt_read_local_oob_data_reply_complete(u16 index, u8 *hash, u8 *randomizer,
-                                                               u8 status)
+int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
+                                               u8 *randomizer, u8 status)
 {
        struct pending_cmd *cmd;
        int err;
 
-       BT_DBG("hci%u status %u", index, status);
+       BT_DBG("%s status %u", hdev->name, status);
 
-       cmd = mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, index);
+       cmd = mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev);
        if (!cmd)
                return -ENOENT;
 
        if (status) {
-               err = cmd_status(cmd->sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
-                                                                       EIO);
+               err = cmd_status(cmd->sk, hdev->id,
+                                       MGMT_OP_READ_LOCAL_OOB_DATA, EIO);
        } else {
                struct mgmt_rp_read_local_oob_data rp;
 
                memcpy(rp.hash, hash, sizeof(rp.hash));
                memcpy(rp.randomizer, randomizer, sizeof(rp.randomizer));
 
-               err = cmd_complete(cmd->sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
-                                                       &rp, sizeof(rp));
+               err = cmd_complete(cmd->sk, hdev->id,
+                                               MGMT_OP_READ_LOCAL_OOB_DATA,
+                                               &rp, sizeof(rp));
        }
 
        mgmt_pending_remove(cmd);
@@ -2314,14 +2342,15 @@ int mgmt_read_local_oob_data_reply_complete(u16 index, u8 *hash, u8 *randomizer,
        return err;
 }
 
-int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 *dev_class, s8 rssi,
-                                                               u8 *eir)
+int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type,
+                                       u8 *dev_class, s8 rssi, u8 *eir)
 {
        struct mgmt_ev_device_found ev;
 
        memset(&ev, 0, sizeof(ev));
 
-       bacpy(&ev.bdaddr, bdaddr);
+       bacpy(&ev.addr.bdaddr, bdaddr);
+       ev.addr.type = link_to_mgmt(type);
        ev.rssi = rssi;
 
        if (eir)
@@ -2330,10 +2359,10 @@ int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 *dev_class, s8 rssi,
        if (dev_class)
                memcpy(ev.dev_class, dev_class, sizeof(ev.dev_class));
 
-       return mgmt_event(MGMT_EV_DEVICE_FOUND, index, &ev, sizeof(ev), NULL);
+       return mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, &ev, sizeof(ev), NULL);
 }
 
-int mgmt_remote_name(u16 index, bdaddr_t *bdaddr, u8 *name)
+int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *name)
 {
        struct mgmt_ev_remote_name ev;
 
@@ -2342,37 +2371,64 @@ int mgmt_remote_name(u16 index, bdaddr_t *bdaddr, u8 *name)
        bacpy(&ev.bdaddr, bdaddr);
        memcpy(ev.name, name, HCI_MAX_NAME_LENGTH);
 
-       return mgmt_event(MGMT_EV_REMOTE_NAME, index, &ev, sizeof(ev), NULL);
+       return mgmt_event(MGMT_EV_REMOTE_NAME, hdev, &ev, sizeof(ev), NULL);
 }
 
-int mgmt_discovering(u16 index, u8 discovering)
+int mgmt_inquiry_failed(struct hci_dev *hdev, u8 status)
 {
-       return mgmt_event(MGMT_EV_DISCOVERING, index, &discovering,
+       struct pending_cmd *cmd;
+       int err;
+
+       cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev);
+       if (!cmd)
+               return -ENOENT;
+
+       err = cmd_status(cmd->sk, hdev->id, cmd->opcode, status);
+       mgmt_pending_remove(cmd);
+
+       return err;
+}
+
+int mgmt_discovering(struct hci_dev *hdev, u8 discovering)
+{
+       struct pending_cmd *cmd;
+
+       if (discovering)
+               cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev);
+       else
+               cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev);
+
+       if (cmd != NULL) {
+               cmd_complete(cmd->sk, hdev->id, cmd->opcode, NULL, 0);
+               mgmt_pending_remove(cmd);
+       }
+
+       return mgmt_event(MGMT_EV_DISCOVERING, hdev, &discovering,
                                                sizeof(discovering), NULL);
 }
 
-int mgmt_device_blocked(u16 index, bdaddr_t *bdaddr)
+int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr)
 {
        struct pending_cmd *cmd;
        struct mgmt_ev_device_blocked ev;
 
-       cmd = mgmt_pending_find(MGMT_OP_BLOCK_DEVICE, index);
+       cmd = mgmt_pending_find(MGMT_OP_BLOCK_DEVICE, hdev);
 
        bacpy(&ev.bdaddr, bdaddr);
 
-       return mgmt_event(MGMT_EV_DEVICE_BLOCKED, index, &ev, sizeof(ev),
-                                               cmd ? cmd->sk : NULL);
+       return mgmt_event(MGMT_EV_DEVICE_BLOCKED, hdev, &ev, sizeof(ev),
+                                                       cmd ? cmd->sk : NULL);
 }
 
-int mgmt_device_unblocked(u16 index, bdaddr_t *bdaddr)
+int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr)
 {
        struct pending_cmd *cmd;
        struct mgmt_ev_device_unblocked ev;
 
-       cmd = mgmt_pending_find(MGMT_OP_UNBLOCK_DEVICE, index);
+       cmd = mgmt_pending_find(MGMT_OP_UNBLOCK_DEVICE, hdev);
 
        bacpy(&ev.bdaddr, bdaddr);
 
-       return mgmt_event(MGMT_EV_DEVICE_UNBLOCKED, index, &ev, sizeof(ev),
-                                               cmd ? cmd->sk : NULL);
+       return mgmt_event(MGMT_EV_DEVICE_UNBLOCKED, hdev, &ev, sizeof(ev),
+                                                       cmd ? cmd->sk : NULL);
 }
index 4e32e18211f9187d8a98a27772a9ba97f5cf7112..8743f369ed3fac82dc80095eb2df8bf8bf6da1b8 100644 (file)
@@ -65,7 +65,8 @@ static DEFINE_MUTEX(rfcomm_mutex);
 
 static LIST_HEAD(session_list);
 
-static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len);
+static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len,
+                                                       u32 priority);
 static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci);
 static int rfcomm_send_disc(struct rfcomm_session *s, u8 dlci);
 static int rfcomm_queue_disc(struct rfcomm_dlc *d);
@@ -377,13 +378,11 @@ static void rfcomm_dlc_unlink(struct rfcomm_dlc *d)
 static struct rfcomm_dlc *rfcomm_dlc_get(struct rfcomm_session *s, u8 dlci)
 {
        struct rfcomm_dlc *d;
-       struct list_head *p;
 
-       list_for_each(p, &s->dlcs) {
-               d = list_entry(p, struct rfcomm_dlc, list);
+       list_for_each_entry(d, &s->dlcs, list)
                if (d->dlci == dlci)
                        return d;
-       }
+
        return NULL;
 }
 
@@ -749,19 +748,34 @@ void rfcomm_session_getaddr(struct rfcomm_session *s, bdaddr_t *src, bdaddr_t *d
 }
 
 /* ---- RFCOMM frame sending ---- */
-static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len)
+static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len,
+                                                       u32 priority)
 {
        struct socket *sock = s->sock;
+       struct sock *sk = sock->sk;
        struct kvec iv = { data, len };
        struct msghdr msg;
 
-       BT_DBG("session %p len %d", s, len);
+       BT_DBG("session %p len %d priority %u", s, len, priority);
+
+       if (sk->sk_priority != priority) {
+               lock_sock(sk);
+               sk->sk_priority = priority;
+               release_sock(sk);
+       }
 
        memset(&msg, 0, sizeof(msg));
 
        return kernel_sendmsg(sock, &msg, &iv, 1, len);
 }
 
+static int rfcomm_send_cmd(struct rfcomm_session *s, struct rfcomm_cmd *cmd)
+{
+       BT_DBG("%p cmd %u", s, cmd->ctrl);
+
+       return rfcomm_send_frame(s, (void *) cmd, sizeof(*cmd), HCI_PRIO_MAX);
+}
+
 static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci)
 {
        struct rfcomm_cmd cmd;
@@ -773,7 +787,7 @@ static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci)
        cmd.len  = __len8(0);
        cmd.fcs  = __fcs2((u8 *) &cmd);
 
-       return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd));
+       return rfcomm_send_cmd(s, &cmd);
 }
 
 static int rfcomm_send_ua(struct rfcomm_session *s, u8 dlci)
@@ -787,7 +801,7 @@ static int rfcomm_send_ua(struct rfcomm_session *s, u8 dlci)
        cmd.len  = __len8(0);
        cmd.fcs  = __fcs2((u8 *) &cmd);
 
-       return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd));
+       return rfcomm_send_cmd(s, &cmd);
 }
 
 static int rfcomm_send_disc(struct rfcomm_session *s, u8 dlci)
@@ -801,7 +815,7 @@ static int rfcomm_send_disc(struct rfcomm_session *s, u8 dlci)
        cmd.len  = __len8(0);
        cmd.fcs  = __fcs2((u8 *) &cmd);
 
-       return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd));
+       return rfcomm_send_cmd(s, &cmd);
 }
 
 static int rfcomm_queue_disc(struct rfcomm_dlc *d)
@@ -815,6 +829,8 @@ static int rfcomm_queue_disc(struct rfcomm_dlc *d)
        if (!skb)
                return -ENOMEM;
 
+       skb->priority = HCI_PRIO_MAX;
+
        cmd = (void *) __skb_put(skb, sizeof(*cmd));
        cmd->addr = d->addr;
        cmd->ctrl = __ctrl(RFCOMM_DISC, 1);
@@ -837,7 +853,7 @@ static int rfcomm_send_dm(struct rfcomm_session *s, u8 dlci)
        cmd.len  = __len8(0);
        cmd.fcs  = __fcs2((u8 *) &cmd);
 
-       return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd));
+       return rfcomm_send_cmd(s, &cmd);
 }
 
 static int rfcomm_send_nsc(struct rfcomm_session *s, int cr, u8 type)
@@ -862,7 +878,7 @@ static int rfcomm_send_nsc(struct rfcomm_session *s, int cr, u8 type)
 
        *ptr = __fcs(buf); ptr++;
 
-       return rfcomm_send_frame(s, buf, ptr - buf);
+       return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX);
 }
 
 static int rfcomm_send_pn(struct rfcomm_session *s, int cr, struct rfcomm_dlc *d)
@@ -904,7 +920,7 @@ static int rfcomm_send_pn(struct rfcomm_session *s, int cr, struct rfcomm_dlc *d
 
        *ptr = __fcs(buf); ptr++;
 
-       return rfcomm_send_frame(s, buf, ptr - buf);
+       return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX);
 }
 
 int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci,
@@ -942,7 +958,7 @@ int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci,
 
        *ptr = __fcs(buf); ptr++;
 
-       return rfcomm_send_frame(s, buf, ptr - buf);
+       return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX);
 }
 
 static int rfcomm_send_rls(struct rfcomm_session *s, int cr, u8 dlci, u8 status)
@@ -969,7 +985,7 @@ static int rfcomm_send_rls(struct rfcomm_session *s, int cr, u8 dlci, u8 status)
 
        *ptr = __fcs(buf); ptr++;
 
-       return rfcomm_send_frame(s, buf, ptr - buf);
+       return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX);
 }
 
 static int rfcomm_send_msc(struct rfcomm_session *s, int cr, u8 dlci, u8 v24_sig)
@@ -996,7 +1012,7 @@ static int rfcomm_send_msc(struct rfcomm_session *s, int cr, u8 dlci, u8 v24_sig
 
        *ptr = __fcs(buf); ptr++;
 
-       return rfcomm_send_frame(s, buf, ptr - buf);
+       return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX);
 }
 
 static int rfcomm_send_fcoff(struct rfcomm_session *s, int cr)
@@ -1018,7 +1034,7 @@ static int rfcomm_send_fcoff(struct rfcomm_session *s, int cr)
 
        *ptr = __fcs(buf); ptr++;
 
-       return rfcomm_send_frame(s, buf, ptr - buf);
+       return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX);
 }
 
 static int rfcomm_send_fcon(struct rfcomm_session *s, int cr)
@@ -1040,7 +1056,7 @@ static int rfcomm_send_fcon(struct rfcomm_session *s, int cr)
 
        *ptr = __fcs(buf); ptr++;
 
-       return rfcomm_send_frame(s, buf, ptr - buf);
+       return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX);
 }
 
 static int rfcomm_send_test(struct rfcomm_session *s, int cr, u8 *pattern, int len)
@@ -1091,7 +1107,7 @@ static int rfcomm_send_credits(struct rfcomm_session *s, u8 addr, u8 credits)
 
        *ptr = __fcs(buf); ptr++;
 
-       return rfcomm_send_frame(s, buf, ptr - buf);
+       return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX);
 }
 
 static void rfcomm_make_uih(struct sk_buff *skb, u8 addr)
@@ -1769,7 +1785,8 @@ static inline int rfcomm_process_tx(struct rfcomm_dlc *d)
                return skb_queue_len(&d->tx_queue);
 
        while (d->tx_credits && (skb = skb_dequeue(&d->tx_queue))) {
-               err = rfcomm_send_frame(d->session, skb->data, skb->len);
+               err = rfcomm_send_frame(d->session, skb->data, skb->len,
+                                                       skb->priority);
                if (err < 0) {
                        skb_queue_head(&d->tx_queue, skb);
                        break;
@@ -2120,15 +2137,13 @@ static struct hci_cb rfcomm_cb = {
 static int rfcomm_dlc_debugfs_show(struct seq_file *f, void *x)
 {
        struct rfcomm_session *s;
-       struct list_head *pp, *p;
 
        rfcomm_lock();
 
-       list_for_each(p, &session_list) {
-               s = list_entry(p, struct rfcomm_session, list);
-               list_for_each(pp, &s->dlcs) {
+       list_for_each_entry(s, &session_list, list) {
+               struct rfcomm_dlc *d;
+               list_for_each_entry(d, &s->dlcs, list) {
                        struct sock *sk = s->sock->sk;
-                       struct rfcomm_dlc *d = list_entry(pp, struct rfcomm_dlc, list);
 
                        seq_printf(f, "%s %s %ld %d %d %d %d\n",
                                                batostr(&bt_sk(sk)->src),
index 5417f6127323ec0db7cb51d75eb1e643fb8ec339..aea2bdd1510f2b4f083a4581105228b927cde858 100644 (file)
@@ -600,6 +600,8 @@ static int rfcomm_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
                        break;
                }
 
+               skb->priority = sk->sk_priority;
+
                err = rfcomm_dlc_send(d, skb);
                if (err < 0) {
                        kfree_skb(skb);
index c258796313e08274c6005e8142ca3304046c5a70..fa8f4de53b997213b05a528434bf5ef14374651d 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/capability.h>
 #include <linux/slab.h>
 #include <linux/skbuff.h>
+#include <linux/workqueue.h>
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
@@ -65,7 +66,7 @@ struct rfcomm_dev {
        struct rfcomm_dlc       *dlc;
        struct tty_struct       *tty;
        wait_queue_head_t       wait;
-       struct tasklet_struct   wakeup_task;
+       struct work_struct      wakeup_task;
 
        struct device           *tty_dev;
 
@@ -81,7 +82,7 @@ static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb);
 static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err);
 static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig);
 
-static void rfcomm_tty_wakeup(unsigned long arg);
+static void rfcomm_tty_wakeup(struct work_struct *work);
 
 /* ---- Device functions ---- */
 static void rfcomm_dev_destruct(struct rfcomm_dev *dev)
@@ -133,13 +134,10 @@ static inline void rfcomm_dev_put(struct rfcomm_dev *dev)
 static struct rfcomm_dev *__rfcomm_dev_get(int id)
 {
        struct rfcomm_dev *dev;
-       struct list_head  *p;
 
-       list_for_each(p, &rfcomm_dev_list) {
-               dev = list_entry(p, struct rfcomm_dev, list);
+       list_for_each_entry(dev, &rfcomm_dev_list, list)
                if (dev->id == id)
                        return dev;
-       }
 
        return NULL;
 }
@@ -197,7 +195,7 @@ static DEVICE_ATTR(channel, S_IRUGO, show_channel, NULL);
 
 static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
 {
-       struct rfcomm_dev *dev;
+       struct rfcomm_dev *dev, *entry;
        struct list_head *head = &rfcomm_dev_list, *p;
        int err = 0;
 
@@ -212,8 +210,8 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
        if (req->dev_id < 0) {
                dev->id = 0;
 
-               list_for_each(p, &rfcomm_dev_list) {
-                       if (list_entry(p, struct rfcomm_dev, list)->id != dev->id)
+               list_for_each_entry(entry, &rfcomm_dev_list, list) {
+                       if (entry->id != dev->id)
                                break;
 
                        dev->id++;
@@ -222,9 +220,7 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
        } else {
                dev->id = req->dev_id;
 
-               list_for_each(p, &rfcomm_dev_list) {
-                       struct rfcomm_dev *entry = list_entry(p, struct rfcomm_dev, list);
-
+               list_for_each_entry(entry, &rfcomm_dev_list, list) {
                        if (entry->id == dev->id) {
                                err = -EADDRINUSE;
                                goto out;
@@ -257,7 +253,7 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
        atomic_set(&dev->opened, 0);
 
        init_waitqueue_head(&dev->wait);
-       tasklet_init(&dev->wakeup_task, rfcomm_tty_wakeup, (unsigned long) dev);
+       INIT_WORK(&dev->wakeup_task, rfcomm_tty_wakeup);
 
        skb_queue_head_init(&dev->pending);
 
@@ -351,7 +347,7 @@ static void rfcomm_wfree(struct sk_buff *skb)
        struct rfcomm_dev *dev = (void *) skb->sk;
        atomic_sub(skb->truesize, &dev->wmem_alloc);
        if (test_bit(RFCOMM_TTY_ATTACHED, &dev->flags))
-               tasklet_schedule(&dev->wakeup_task);
+               queue_work(system_nrt_wq, &dev->wakeup_task);
        rfcomm_dev_put(dev);
 }
 
@@ -455,9 +451,9 @@ static int rfcomm_release_dev(void __user *arg)
 
 static int rfcomm_get_dev_list(void __user *arg)
 {
+       struct rfcomm_dev *dev;
        struct rfcomm_dev_list_req *dl;
        struct rfcomm_dev_info *di;
-       struct list_head *p;
        int n = 0, size, err;
        u16 dev_num;
 
@@ -479,8 +475,7 @@ static int rfcomm_get_dev_list(void __user *arg)
 
        read_lock_bh(&rfcomm_dev_lock);
 
-       list_for_each(p, &rfcomm_dev_list) {
-               struct rfcomm_dev *dev = list_entry(p, struct rfcomm_dev, list);
+       list_for_each_entry(dev, &rfcomm_dev_list, list) {
                if (test_bit(RFCOMM_TTY_RELEASED, &dev->flags))
                        continue;
                (di + n)->id      = dev->id;
@@ -635,9 +630,10 @@ static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig)
 }
 
 /* ---- TTY functions ---- */
-static void rfcomm_tty_wakeup(unsigned long arg)
+static void rfcomm_tty_wakeup(struct work_struct *work)
 {
-       struct rfcomm_dev *dev = (void *) arg;
+       struct rfcomm_dev *dev = container_of(work, struct rfcomm_dev,
+                                                               wakeup_task);
        struct tty_struct *tty = dev->tty;
        if (!tty)
                return;
@@ -762,7 +758,7 @@ static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp)
                rfcomm_dlc_close(dev->dlc, 0);
 
                clear_bit(RFCOMM_TTY_ATTACHED, &dev->flags);
-               tasklet_kill(&dev->wakeup_task);
+               cancel_work_sync(&dev->wakeup_task);
 
                rfcomm_dlc_lock(dev->dlc);
                tty->driver_data = NULL;
@@ -1155,9 +1151,11 @@ static const struct tty_operations rfcomm_ops = {
 
 int __init rfcomm_init_ttys(void)
 {
+       int error;
+
        rfcomm_tty_driver = alloc_tty_driver(RFCOMM_TTY_PORTS);
        if (!rfcomm_tty_driver)
-               return -1;
+               return -ENOMEM;
 
        rfcomm_tty_driver->owner        = THIS_MODULE;
        rfcomm_tty_driver->driver_name  = "rfcomm";
@@ -1172,10 +1170,11 @@ int __init rfcomm_init_ttys(void)
        rfcomm_tty_driver->init_termios.c_lflag &= ~ICANON;
        tty_set_operations(rfcomm_tty_driver, &rfcomm_ops);
 
-       if (tty_register_driver(rfcomm_tty_driver)) {
+       error = tty_register_driver(rfcomm_tty_driver);
+       if (error) {
                BT_ERR("Can't register RFCOMM TTY driver");
                put_tty_driver(rfcomm_tty_driver);
-               return -1;
+               return error;
        }
 
        BT_INFO("RFCOMM TTY layer initialized");
index 759b63572641012b4c89fa5f48fcf6c68eee6918..94e94ca353848768e5bca28c3364a7b8554acdf3 100644 (file)
@@ -181,7 +181,8 @@ static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data)
        if (!skb)
                return;
 
-       hci_send_acl(conn->hcon, skb, 0);
+       skb->priority = HCI_PRIO_MAX;
+       hci_send_acl(conn->hchan, skb, 0);
 
        mod_timer(&conn->security_timer, jiffies +
                                        msecs_to_jiffies(SMP_TIMEOUT));