Initial Contribution
msm-2.6.38: tag AU_LINUX_ANDROID_GINGERBREAD.02.03.04.00.142
Signed-off-by: Bryan Huntsman <bryanh@codeaurora.org>
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 33c4e0c..0ecb942 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -1,6 +1,6 @@
/*
BlueZ - Bluetooth protocol stack for Linux
- Copyright (c) 2000-2001, 2010, Code Aurora Forum. All rights reserved.
+ Copyright (c) 2000-2001, 2010-2011 Code Aurora Forum. All rights reserved.
Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
@@ -132,15 +132,22 @@
void hci_acl_disconn(struct hci_conn *conn, __u8 reason)
{
- struct hci_cp_disconnect cp;
-
BT_DBG("%p", conn);
conn->state = BT_DISCONN;
- cp.handle = cpu_to_le16(conn->handle);
- cp.reason = reason;
- hci_send_cmd(conn->hdev, HCI_OP_DISCONNECT, sizeof(cp), &cp);
+ if (conn->hdev->dev_type == HCI_BREDR) {
+ struct hci_cp_disconnect cp;
+ cp.handle = cpu_to_le16(conn->handle);
+ cp.reason = reason;
+ hci_send_cmd(conn->hdev, HCI_OP_DISCONNECT, sizeof(cp), &cp);
+ } else {
+ struct hci_cp_disconn_phys_link cp;
+ cp.phy_handle = (u8) conn->handle;
+ cp.reason = reason;
+ hci_send_cmd(conn->hdev, HCI_OP_DISCONN_PHYS_LINK,
+ sizeof(cp), &cp);
+ }
}
void hci_add_sco(struct hci_conn *conn, __u16 handle)
@@ -178,9 +185,9 @@
cp.tx_bandwidth = cpu_to_le32(0x00001f40);
cp.rx_bandwidth = cpu_to_le32(0x00001f40);
- cp.max_latency = cpu_to_le16(0xffff);
+ cp.max_latency = cpu_to_le16(0x000A);
cp.voice_setting = cpu_to_le16(hdev->voice_setting);
- cp.retrans_effort = 0xff;
+ cp.retrans_effort = 0x01;
hci_send_cmd(hdev, HCI_OP_SETUP_SYNC_CONN, sizeof(cp), &cp);
}
@@ -320,19 +327,6 @@
hci_conn_enter_sniff_mode(conn);
}
-static void hci_conn_auto_accept(unsigned long arg)
-{
- struct hci_conn *conn = (void *) arg;
- struct hci_dev *hdev = conn->hdev;
-
- hci_dev_lock(hdev);
-
- hci_send_cmd(hdev, HCI_OP_USER_CONFIRM_REPLY, sizeof(conn->dst),
- &conn->dst);
-
- hci_dev_unlock(hdev);
-}
-
struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type,
__u16 pkt_type, bdaddr_t *dst)
{
@@ -352,7 +346,6 @@
conn->auth_type = HCI_AT_GENERAL_BONDING;
conn->io_capability = hdev->io_capability;
conn->remote_auth = 0xff;
- conn->key_type = 0xff;
conn->power_save = 1;
conn->disc_timeout = HCI_DISCONN_TIMEOUT;
@@ -385,8 +378,6 @@
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,
- (unsigned long) conn);
atomic_set(&conn->refcnt, 0);
@@ -407,6 +398,18 @@
return conn;
}
+struct hci_conn *hci_le_conn_add(struct hci_dev *hdev, bdaddr_t *dst,
+ __u8 addr_type)
+{
+ struct hci_conn *conn = hci_conn_add(hdev, LE_LINK, 0, dst);
+ if (!conn)
+ return NULL;
+
+ conn->dst_type = addr_type;
+
+ return conn;
+}
+
int hci_conn_del(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
@@ -417,8 +420,6 @@
del_timer(&conn->disc_timer);
- del_timer(&conn->auto_accept_timer);
-
if (conn->type == ACL_LINK) {
struct hci_conn *sco = conn->link;
if (sco)
@@ -453,12 +454,62 @@
hci_dev_put(hdev);
- if (conn->handle == 0)
- kfree(conn);
+ return 0;
+}
+
+struct hci_chan *hci_chan_add(struct hci_dev *hdev)
+{
+ struct hci_chan *chan;
+
+ BT_DBG("%s", hdev->name);
+
+ chan = kzalloc(sizeof(struct hci_chan), GFP_ATOMIC);
+ if (!chan)
+ return NULL;
+
+ atomic_set(&chan->refcnt, 0);
+
+ hci_dev_hold(hdev);
+
+ chan->hdev = hdev;
+
+ list_add(&chan->list, &hdev->chan_list.list);
+
+ return chan;
+}
+
+int hci_chan_del(struct hci_chan *chan)
+{
+ BT_DBG("%s chan %p", chan->hdev->name, chan);
+
+ list_del(&chan->list);
+
+ hci_conn_put(chan->conn);
+ hci_dev_put(chan->hdev);
+
+ kfree(chan);
return 0;
}
+void hci_chan_put(struct hci_chan *chan)
+{
+ struct hci_cp_disconn_logical_link cp;
+
+ BT_DBG("chan %p refcnt %d", chan, atomic_read(&chan->refcnt));
+ if (!atomic_dec_and_test(&chan->refcnt))
+ return;
+
+ BT_DBG("chan->conn->state %d", chan->conn->state);
+ if (chan->conn->state == BT_CONNECTED) {
+ cp.log_handle = cpu_to_le16(chan->ll_handle);
+ hci_send_cmd(chan->conn->hdev, HCI_OP_DISCONN_LOGICAL_LINK,
+ sizeof(cp), &cp);
+ } else
+ hci_chan_del(chan);
+}
+EXPORT_SYMBOL(hci_chan_put);
+
struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src)
{
int use_src = bacmp(src, BDADDR_ANY);
@@ -472,6 +523,8 @@
list_for_each(p, &hci_dev_list) {
struct hci_dev *d = list_entry(p, struct hci_dev, list);
+ if (d->dev_type != HCI_BREDR)
+ continue;
if (!test_bit(HCI_UP, &d->flags) || test_bit(HCI_RAW, &d->flags))
continue;
@@ -499,6 +552,58 @@
}
EXPORT_SYMBOL(hci_get_route);
+struct hci_dev *hci_dev_get_type(u8 amp_type)
+{
+ struct hci_dev *hdev = NULL;
+ struct hci_dev *d;
+
+ BT_DBG("amp_type %d", amp_type);
+
+ read_lock_bh(&hci_dev_list_lock);
+
+ list_for_each_entry(d, &hci_dev_list, list) {
+ if ((d->amp_type == amp_type) && test_bit(HCI_UP, &d->flags)) {
+ hdev = d;
+ break;
+ }
+ }
+
+ if (hdev)
+ hdev = hci_dev_hold(hdev);
+
+ read_unlock_bh(&hci_dev_list_lock);
+ return hdev;
+}
+EXPORT_SYMBOL(hci_dev_get_type);
+
+struct hci_dev *hci_dev_get_amp(bdaddr_t *dst)
+{
+ struct hci_dev *d;
+ struct hci_dev *hdev = NULL;
+
+ BT_DBG("%s dst %s", hdev->name, batostr(dst));
+
+ read_lock_bh(&hci_dev_list_lock);
+
+ list_for_each_entry(d, &hci_dev_list, list) {
+ struct hci_conn *conn;
+ if (d->dev_type == HCI_BREDR)
+ continue;
+ conn = hci_conn_hash_lookup_ba(d, ACL_LINK, dst);
+ if (conn) {
+ hdev = d;
+ break;
+ }
+ }
+
+ if (hdev)
+ hdev = hci_dev_hold(hdev);
+
+ read_unlock_bh(&hci_dev_list_lock);
+ return hdev;
+}
+EXPORT_SYMBOL(hci_dev_get_amp);
+
/* Create SCO, ACL or LE connection.
* Device _must_ be locked */
struct hci_conn *hci_connect(struct hci_dev *hdev, int type,
@@ -522,12 +627,10 @@
if (!entry)
return ERR_PTR(-EHOSTUNREACH);
- le = hci_conn_add(hdev, LE_LINK, 0, dst);
+ le = hci_le_conn_add(hdev, dst, entry->bdaddr_type);
if (!le)
return ERR_PTR(-ENOMEM);
- le->dst_type = entry->bdaddr_type;
-
hci_le_connect(le);
hci_conn_hold(le);
@@ -571,7 +674,7 @@
if (acl->state == BT_CONNECTED &&
(sco->state == BT_OPEN || sco->state == BT_CLOSED)) {
acl->power_save = 1;
- hci_conn_enter_active_mode(acl, BT_POWER_FORCE_ACTIVE_ON);
+ hci_conn_enter_active_mode(acl, 1);
if (test_bit(HCI_CONN_MODE_CHANGE_PEND, &acl->pend)) {
/* defer SCO setup until mode change completed */
@@ -586,6 +689,36 @@
}
EXPORT_SYMBOL(hci_connect);
+void hci_disconnect(struct hci_conn *conn, __u8 reason)
+{
+ BT_DBG("conn %p", conn);
+
+ hci_proto_disconn_cfm(conn, reason);
+}
+EXPORT_SYMBOL(hci_disconnect);
+
+void hci_disconnect_amp(struct hci_conn *conn, __u8 reason)
+{
+ struct hci_dev *hdev = NULL;
+
+ BT_DBG("conn %p", conn);
+
+ read_lock_bh(&hci_dev_list_lock);
+
+ list_for_each_entry(hdev, &hci_dev_list, list) {
+ struct hci_conn *c;
+ if (hdev == conn->hdev)
+ continue;
+ if (hdev->amp_type == HCI_BREDR)
+ continue;
+ c = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &conn->dst);
+ if (c)
+ hci_disconnect(c, reason);
+ }
+
+ read_unlock_bh(&hci_dev_list_lock);
+}
+
/* Check link security requirement */
int hci_conn_check_link_mode(struct hci_conn *conn)
{
@@ -619,105 +752,44 @@
if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->pend)) {
struct hci_cp_auth_requested cp;
-
- /* encrypt must be pending if auth is also pending */
- set_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend);
-
cp.handle = cpu_to_le16(conn->handle);
hci_send_cmd(conn->hdev, HCI_OP_AUTH_REQUESTED,
sizeof(cp), &cp);
- if (conn->key_type != 0xff)
- set_bit(HCI_CONN_REAUTH_PEND, &conn->pend);
}
return 0;
}
-/* Encrypt the the link */
-static void hci_conn_encrypt(struct hci_conn *conn)
-{
- BT_DBG("conn %p", conn);
-
- if (!test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) {
- struct hci_cp_set_conn_encrypt cp;
- cp.handle = cpu_to_le16(conn->handle);
- cp.encrypt = 0x01;
- hci_send_cmd(conn->hdev, HCI_OP_SET_CONN_ENCRYPT, sizeof(cp),
- &cp);
- }
-}
-
/* Enable security */
int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
{
BT_DBG("conn %p", conn);
- /* For sdp we don't need the link key. */
if (sec_level == BT_SECURITY_SDP)
return 1;
- /* For non 2.1 devices and low security level we don't need the link
- key. */
if (sec_level == BT_SECURITY_LOW &&
(!conn->ssp_mode || !conn->hdev->ssp_mode))
return 1;
- /* For other security levels we need the link key. */
- if (!(conn->link_mode & HCI_LM_AUTH))
- goto auth;
-
- /* An authenticated combination key has sufficient security for any
- security level. */
- if (conn->key_type == HCI_LK_AUTH_COMBINATION)
- goto encrypt;
-
- /* An unauthenticated combination key has sufficient security for
- security level 1 and 2. */
- if (conn->key_type == HCI_LK_UNAUTH_COMBINATION &&
- (sec_level == BT_SECURITY_MEDIUM ||
- sec_level == BT_SECURITY_LOW))
- goto encrypt;
-
- /* A combination key has always sufficient security for the security
- levels 1 or 2. High security level requires the combination key
- is generated using maximum PIN code length (16).
- For pre 2.1 units. */
- if (conn->key_type == HCI_LK_COMBINATION &&
- (sec_level != BT_SECURITY_HIGH ||
- conn->pin_length == 16))
- goto encrypt;
-
-auth:
- if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend))
- return 0;
-
- if (!hci_conn_auth(conn, sec_level, auth_type))
- return 0;
-
-encrypt:
if (conn->link_mode & HCI_LM_ENCRYPT)
- return 1;
+ return hci_conn_auth(conn, sec_level, auth_type);
- hci_conn_encrypt(conn);
+ if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend))
+ return 0;
+
+ if (hci_conn_auth(conn, sec_level, auth_type)) {
+ struct hci_cp_set_conn_encrypt cp;
+ cp.handle = cpu_to_le16(conn->handle);
+ cp.encrypt = 1;
+ hci_send_cmd(conn->hdev, HCI_OP_SET_CONN_ENCRYPT,
+ sizeof(cp), &cp);
+ }
+
return 0;
}
EXPORT_SYMBOL(hci_conn_security);
-/* Check secure link requirement */
-int hci_conn_check_secure(struct hci_conn *conn, __u8 sec_level)
-{
- BT_DBG("conn %p", conn);
-
- if (sec_level != BT_SECURITY_HIGH)
- return 1; /* Accept if non-secure is required */
-
- if (conn->sec_level == BT_SECURITY_HIGH)
- return 1;
-
- return 0; /* Reject not secure link */
-}
-EXPORT_SYMBOL(hci_conn_check_secure);
-
/* Change link key */
int hci_conn_change_link_key(struct hci_conn *conn)
{
@@ -817,6 +889,98 @@
}
}
+struct hci_chan *hci_chan_accept(struct hci_conn *conn,
+ struct hci_ext_fs *tx_fs, struct hci_ext_fs *rx_fs)
+{
+ struct hci_chan *chan;
+ struct hci_cp_create_logical_link cp;
+
+ chan = hci_chan_add(conn->hdev);
+ if (!chan)
+ return NULL;
+
+ chan->state = BT_CONNECT;
+ chan->conn = conn;
+ chan->tx_fs = *tx_fs;
+ chan->rx_fs = *rx_fs;
+ cp.phy_handle = chan->conn->handle;
+ cp.tx_fs.id = chan->tx_fs.id;
+ cp.tx_fs.type = chan->tx_fs.type;
+ cp.tx_fs.max_sdu = cpu_to_le16(chan->tx_fs.max_sdu);
+ cp.tx_fs.sdu_arr_time = cpu_to_le32(chan->tx_fs.sdu_arr_time);
+ cp.tx_fs.acc_latency = cpu_to_le32(chan->tx_fs.acc_latency);
+ cp.tx_fs.flush_to = cpu_to_le32(chan->tx_fs.flush_to);
+ cp.rx_fs.id = chan->rx_fs.id;
+ cp.rx_fs.type = chan->rx_fs.type;
+ cp.rx_fs.max_sdu = cpu_to_le16(chan->rx_fs.max_sdu);
+ cp.rx_fs.sdu_arr_time = cpu_to_le32(chan->rx_fs.sdu_arr_time);
+ cp.rx_fs.acc_latency = cpu_to_le32(chan->rx_fs.acc_latency);
+ cp.rx_fs.flush_to = cpu_to_le32(chan->rx_fs.flush_to);
+ hci_conn_hold(chan->conn);
+ hci_send_cmd(conn->hdev, HCI_OP_ACCEPT_LOGICAL_LINK, sizeof(cp), &cp);
+ return chan;
+}
+EXPORT_SYMBOL(hci_chan_accept);
+
+struct hci_chan *hci_chan_create(struct hci_conn *conn,
+ struct hci_ext_fs *tx_fs, struct hci_ext_fs *rx_fs)
+{
+ struct hci_chan *chan;
+ struct hci_cp_create_logical_link cp;
+
+ chan = hci_chan_add(conn->hdev);
+ if (!chan)
+ return NULL;
+
+ chan->state = BT_CONNECT;
+ chan->conn = conn;
+ chan->tx_fs = *tx_fs;
+ chan->rx_fs = *rx_fs;
+ cp.phy_handle = chan->conn->handle;
+ cp.tx_fs.id = chan->tx_fs.id;
+ cp.tx_fs.type = chan->tx_fs.type;
+ cp.tx_fs.max_sdu = cpu_to_le16(chan->tx_fs.max_sdu);
+ cp.tx_fs.sdu_arr_time = cpu_to_le32(chan->tx_fs.sdu_arr_time);
+ cp.tx_fs.acc_latency = cpu_to_le32(chan->tx_fs.acc_latency);
+ cp.tx_fs.flush_to = cpu_to_le32(chan->tx_fs.flush_to);
+ cp.rx_fs.id = chan->rx_fs.id;
+ cp.rx_fs.type = chan->rx_fs.type;
+ cp.rx_fs.max_sdu = cpu_to_le16(chan->rx_fs.max_sdu);
+ cp.rx_fs.sdu_arr_time = cpu_to_le32(chan->rx_fs.sdu_arr_time);
+ cp.rx_fs.acc_latency = cpu_to_le32(chan->rx_fs.acc_latency);
+ cp.rx_fs.flush_to = cpu_to_le32(chan->rx_fs.flush_to);
+ hci_conn_hold(chan->conn);
+ hci_send_cmd(conn->hdev, HCI_OP_CREATE_LOGICAL_LINK, sizeof(cp), &cp);
+ return chan;
+}
+EXPORT_SYMBOL(hci_chan_create);
+
+void hci_chan_modify(struct hci_chan *chan,
+ struct hci_ext_fs *tx_fs, struct hci_ext_fs *rx_fs)
+{
+ struct hci_cp_flow_spec_modify cp;
+
+ chan->tx_fs = *tx_fs;
+ chan->rx_fs = *rx_fs;
+ cp.log_handle = cpu_to_le16(chan->ll_handle);
+ cp.tx_fs.id = tx_fs->id;
+ cp.tx_fs.type = tx_fs->type;
+ cp.tx_fs.max_sdu = cpu_to_le16(tx_fs->max_sdu);
+ cp.tx_fs.sdu_arr_time = cpu_to_le32(tx_fs->sdu_arr_time);
+ cp.tx_fs.acc_latency = cpu_to_le32(tx_fs->acc_latency);
+ cp.tx_fs.flush_to = cpu_to_le32(tx_fs->flush_to);
+ cp.rx_fs.id = rx_fs->id;
+ cp.rx_fs.type = rx_fs->type;
+ cp.rx_fs.max_sdu = cpu_to_le16(rx_fs->max_sdu);
+ cp.rx_fs.sdu_arr_time = cpu_to_le32(rx_fs->sdu_arr_time);
+ cp.rx_fs.acc_latency = cpu_to_le32(rx_fs->acc_latency);
+ cp.rx_fs.flush_to = cpu_to_le32(rx_fs->flush_to);
+ hci_conn_hold(chan->conn);
+ hci_send_cmd(chan->conn->hdev, HCI_OP_FLOW_SPEC_MODIFY, sizeof(cp),
+ &cp);
+}
+EXPORT_SYMBOL(hci_chan_modify);
+
/* Drop all connection on the device */
void hci_conn_hash_flush(struct hci_dev *hdev)
{
@@ -961,6 +1125,8 @@
ci.cnt = hdev->acl_cnt;
ci.pkts = hdev->acl_pkts;
}
+ ci.pending_sec_level = conn->pending_sec_level;
+ ci.ssp_mode = conn->ssp_mode;
}
hci_dev_unlock_bh(hdev);
@@ -989,3 +1155,23 @@
return copy_to_user(arg, &req, sizeof(req)) ? -EFAULT : 0;
}
+
+int hci_set_auth_info(struct hci_dev *hdev, void __user *arg)
+{
+ struct hci_auth_info_req req;
+ struct hci_conn *conn;
+
+ if (copy_from_user(&req, arg, sizeof(req)))
+ return -EFAULT;
+
+ hci_dev_lock_bh(hdev);
+ conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &req.bdaddr);
+ if (conn)
+ conn->auth_type = req.type;
+ hci_dev_unlock_bh(hdev);
+
+ if (!conn)
+ return -ENOENT;
+
+ return copy_to_user(arg, &req, sizeof(req)) ? -EFAULT : 0;
+}