mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-11-07 12:07:19 -06:00
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6 into for-davem
This commit is contained in:
+5
-11
@@ -27,12 +27,12 @@ menuconfig BT
|
||||
compile it as module (bluetooth).
|
||||
|
||||
To use Linux Bluetooth subsystem, you will need several user-space
|
||||
utilities like hciconfig and hcid. These utilities and updates to
|
||||
Bluetooth kernel modules are provided in the BlueZ packages.
|
||||
For more information, see <http://www.bluez.org/>.
|
||||
utilities like hciconfig and bluetoothd. These utilities and updates
|
||||
to Bluetooth kernel modules are provided in the BlueZ packages. For
|
||||
more information, see <http://www.bluez.org/>.
|
||||
|
||||
config BT_L2CAP
|
||||
tristate "L2CAP protocol support"
|
||||
bool "L2CAP protocol support"
|
||||
depends on BT
|
||||
select CRC16
|
||||
help
|
||||
@@ -40,19 +40,13 @@ config BT_L2CAP
|
||||
connection oriented and connection-less data transport. L2CAP
|
||||
support is required for most Bluetooth applications.
|
||||
|
||||
Say Y here to compile L2CAP support into the kernel or say M to
|
||||
compile it as module (l2cap).
|
||||
|
||||
config BT_SCO
|
||||
tristate "SCO links support"
|
||||
bool "SCO links support"
|
||||
depends on BT
|
||||
help
|
||||
SCO link provides voice transport over Bluetooth. SCO support is
|
||||
required for voice applications like Headset and Audio.
|
||||
|
||||
Say Y here to compile SCO support into the kernel or say M to
|
||||
compile it as module (sco).
|
||||
|
||||
source "net/bluetooth/rfcomm/Kconfig"
|
||||
|
||||
source "net/bluetooth/bnep/Kconfig"
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
#
|
||||
|
||||
obj-$(CONFIG_BT) += bluetooth.o
|
||||
obj-$(CONFIG_BT_L2CAP) += l2cap.o
|
||||
obj-$(CONFIG_BT_SCO) += sco.o
|
||||
obj-$(CONFIG_BT_RFCOMM) += rfcomm/
|
||||
obj-$(CONFIG_BT_BNEP) += bnep/
|
||||
obj-$(CONFIG_BT_CMTP) += cmtp/
|
||||
obj-$(CONFIG_BT_HIDP) += hidp/
|
||||
|
||||
bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o hci_sock.o hci_sysfs.o lib.o
|
||||
bluetooth-$(CONFIG_BT_L2CAP) += l2cap_core.o l2cap_sock.o
|
||||
bluetooth-$(CONFIG_BT_SCO) += sco.o
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
|
||||
#define VERSION "2.15"
|
||||
#define VERSION "2.16"
|
||||
|
||||
/* Bluetooth sockets */
|
||||
#define BT_MAX_PROTO 8
|
||||
@@ -199,14 +199,15 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock)
|
||||
|
||||
BT_DBG("parent %p", parent);
|
||||
|
||||
local_bh_disable();
|
||||
list_for_each_safe(p, n, &bt_sk(parent)->accept_q) {
|
||||
sk = (struct sock *) list_entry(p, struct bt_sock, accept_q);
|
||||
|
||||
lock_sock(sk);
|
||||
bh_lock_sock(sk);
|
||||
|
||||
/* FIXME: Is this check still needed */
|
||||
if (sk->sk_state == BT_CLOSED) {
|
||||
release_sock(sk);
|
||||
bh_unlock_sock(sk);
|
||||
bt_accept_unlink(sk);
|
||||
continue;
|
||||
}
|
||||
@@ -216,12 +217,16 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock)
|
||||
bt_accept_unlink(sk);
|
||||
if (newsock)
|
||||
sock_graft(sk, newsock);
|
||||
release_sock(sk);
|
||||
|
||||
bh_unlock_sock(sk);
|
||||
local_bh_enable();
|
||||
return sk;
|
||||
}
|
||||
|
||||
release_sock(sk);
|
||||
bh_unlock_sock(sk);
|
||||
}
|
||||
local_bh_enable();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(bt_accept_dequeue);
|
||||
@@ -240,7 +245,8 @@ int bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
|
||||
if (flags & (MSG_OOB))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!(skb = skb_recv_datagram(sk, flags, noblock, &err))) {
|
||||
skb = skb_recv_datagram(sk, flags, noblock, &err);
|
||||
if (!skb) {
|
||||
if (sk->sk_shutdown & RCV_SHUTDOWN)
|
||||
return 0;
|
||||
return err;
|
||||
@@ -323,7 +329,8 @@ int bt_sock_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
|
||||
if (copied >= target)
|
||||
break;
|
||||
|
||||
if ((err = sock_error(sk)) != 0)
|
||||
err = sock_error(sk);
|
||||
if (err)
|
||||
break;
|
||||
if (sk->sk_shutdown & RCV_SHUTDOWN)
|
||||
break;
|
||||
@@ -390,7 +397,7 @@ static inline unsigned int bt_accept_poll(struct sock *parent)
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int bt_sock_poll(struct file * file, struct socket *sock, poll_table *wait)
|
||||
unsigned int bt_sock_poll(struct file *file, struct socket *sock, poll_table *wait)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
unsigned int mask = 0;
|
||||
@@ -538,13 +545,41 @@ static int __init bt_init(void)
|
||||
|
||||
BT_INFO("HCI device and connection manager initialized");
|
||||
|
||||
hci_sock_init();
|
||||
err = hci_sock_init();
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = l2cap_init();
|
||||
if (err < 0) {
|
||||
hci_sock_cleanup();
|
||||
goto sock_err;
|
||||
}
|
||||
|
||||
err = sco_init();
|
||||
if (err < 0) {
|
||||
l2cap_exit();
|
||||
goto sock_err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
sock_err:
|
||||
hci_sock_cleanup();
|
||||
|
||||
error:
|
||||
sock_unregister(PF_BLUETOOTH);
|
||||
bt_sysfs_cleanup();
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit bt_exit(void)
|
||||
{
|
||||
|
||||
sco_exit();
|
||||
|
||||
l2cap_exit();
|
||||
|
||||
hci_sock_cleanup();
|
||||
|
||||
sock_unregister(PF_BLUETOOTH);
|
||||
|
||||
@@ -708,8 +708,6 @@ static int __init bnep_init(void)
|
||||
{
|
||||
char flt[50] = "";
|
||||
|
||||
l2cap_load();
|
||||
|
||||
#ifdef CONFIG_BT_BNEP_PROTO_FILTER
|
||||
strcat(flt, "protocol ");
|
||||
#endif
|
||||
|
||||
@@ -88,6 +88,7 @@ static int bnep_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long
|
||||
sockfd_put(nsock);
|
||||
return -EBADFD;
|
||||
}
|
||||
ca.device[sizeof(ca.device)-1] = 0;
|
||||
|
||||
err = bnep_add_connection(&ca, nsock);
|
||||
if (!err) {
|
||||
|
||||
@@ -155,7 +155,8 @@ static void cmtp_send_interopmsg(struct cmtp_session *session,
|
||||
|
||||
BT_DBG("session %p subcmd 0x%02x appl %d msgnum %d", session, subcmd, appl, msgnum);
|
||||
|
||||
if (!(skb = alloc_skb(CAPI_MSG_BASELEN + 6 + len, GFP_ATOMIC))) {
|
||||
skb = alloc_skb(CAPI_MSG_BASELEN + 6 + len, GFP_ATOMIC);
|
||||
if (!skb) {
|
||||
BT_ERR("Can't allocate memory for interoperability packet");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -115,7 +115,8 @@ static inline void cmtp_add_msgpart(struct cmtp_session *session, int id, const
|
||||
|
||||
size = (skb) ? skb->len + count : count;
|
||||
|
||||
if (!(nskb = alloc_skb(size, GFP_ATOMIC))) {
|
||||
nskb = alloc_skb(size, GFP_ATOMIC);
|
||||
if (!nskb) {
|
||||
BT_ERR("Can't allocate memory for CAPI message");
|
||||
return;
|
||||
}
|
||||
@@ -216,7 +217,8 @@ static void cmtp_process_transmit(struct cmtp_session *session)
|
||||
|
||||
BT_DBG("session %p", session);
|
||||
|
||||
if (!(nskb = alloc_skb(session->mtu, GFP_ATOMIC))) {
|
||||
nskb = alloc_skb(session->mtu, GFP_ATOMIC);
|
||||
if (!nskb) {
|
||||
BT_ERR("Can't allocate memory for new frame");
|
||||
return;
|
||||
}
|
||||
@@ -224,7 +226,8 @@ static void cmtp_process_transmit(struct cmtp_session *session)
|
||||
while ((skb = skb_dequeue(&session->transmit))) {
|
||||
struct cmtp_scb *scb = (void *) skb->cb;
|
||||
|
||||
if ((tail = (session->mtu - nskb->len)) < 5) {
|
||||
tail = session->mtu - nskb->len;
|
||||
if (tail < 5) {
|
||||
cmtp_send_frame(session, nskb->data, nskb->len);
|
||||
skb_trim(nskb, 0);
|
||||
tail = session->mtu;
|
||||
@@ -466,8 +469,6 @@ int cmtp_get_conninfo(struct cmtp_conninfo *ci)
|
||||
|
||||
static int __init cmtp_init(void)
|
||||
{
|
||||
l2cap_load();
|
||||
|
||||
BT_INFO("CMTP (CAPI Emulation) ver %s", VERSION);
|
||||
|
||||
cmtp_init_sockets();
|
||||
|
||||
@@ -45,6 +45,33 @@
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
|
||||
static void hci_le_connect(struct hci_conn *conn)
|
||||
{
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
struct hci_cp_le_create_conn cp;
|
||||
|
||||
conn->state = BT_CONNECT;
|
||||
conn->out = 1;
|
||||
conn->link_mode |= HCI_LM_MASTER;
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
cp.scan_interval = cpu_to_le16(0x0004);
|
||||
cp.scan_window = cpu_to_le16(0x0004);
|
||||
bacpy(&cp.peer_addr, &conn->dst);
|
||||
cp.conn_interval_min = cpu_to_le16(0x0008);
|
||||
cp.conn_interval_max = cpu_to_le16(0x0100);
|
||||
cp.supervision_timeout = cpu_to_le16(0x0064);
|
||||
cp.min_ce_len = cpu_to_le16(0x0001);
|
||||
cp.max_ce_len = cpu_to_le16(0x0001);
|
||||
|
||||
hci_send_cmd(hdev, HCI_OP_LE_CREATE_CONN, sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
static void hci_le_connect_cancel(struct hci_conn *conn)
|
||||
{
|
||||
hci_send_cmd(conn->hdev, HCI_OP_LE_CREATE_CONN_CANCEL, 0, NULL);
|
||||
}
|
||||
|
||||
void hci_acl_connect(struct hci_conn *conn)
|
||||
{
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
@@ -156,6 +183,26 @@ void hci_setup_sync(struct hci_conn *conn, __u16 handle)
|
||||
hci_send_cmd(hdev, HCI_OP_SETUP_SYNC_CONN, sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max,
|
||||
u16 latency, u16 to_multiplier)
|
||||
{
|
||||
struct hci_cp_le_conn_update cp;
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
|
||||
cp.handle = cpu_to_le16(conn->handle);
|
||||
cp.conn_interval_min = cpu_to_le16(min);
|
||||
cp.conn_interval_max = cpu_to_le16(max);
|
||||
cp.conn_latency = cpu_to_le16(latency);
|
||||
cp.supervision_timeout = cpu_to_le16(to_multiplier);
|
||||
cp.min_ce_len = cpu_to_le16(0x0001);
|
||||
cp.max_ce_len = cpu_to_le16(0x0001);
|
||||
|
||||
hci_send_cmd(hdev, HCI_OP_LE_CONN_UPDATE, sizeof(cp), &cp);
|
||||
}
|
||||
EXPORT_SYMBOL(hci_le_conn_update);
|
||||
|
||||
/* Device _must_ be locked */
|
||||
void hci_sco_setup(struct hci_conn *conn, __u8 status)
|
||||
{
|
||||
@@ -193,8 +240,12 @@ static void hci_conn_timeout(unsigned long arg)
|
||||
switch (conn->state) {
|
||||
case BT_CONNECT:
|
||||
case BT_CONNECT2:
|
||||
if (conn->type == ACL_LINK && conn->out)
|
||||
hci_acl_connect_cancel(conn);
|
||||
if (conn->out) {
|
||||
if (conn->type == ACL_LINK)
|
||||
hci_acl_connect_cancel(conn);
|
||||
else if (conn->type == LE_LINK)
|
||||
hci_le_connect_cancel(conn);
|
||||
}
|
||||
break;
|
||||
case BT_CONFIG:
|
||||
case BT_CONNECTED:
|
||||
@@ -234,6 +285,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
|
||||
conn->mode = HCI_CM_ACTIVE;
|
||||
conn->state = BT_OPEN;
|
||||
conn->auth_type = HCI_AT_GENERAL_BONDING;
|
||||
conn->io_capability = hdev->io_capability;
|
||||
|
||||
conn->power_save = 1;
|
||||
conn->disc_timeout = HCI_DISCONN_TIMEOUT;
|
||||
@@ -295,6 +347,11 @@ int hci_conn_del(struct hci_conn *conn)
|
||||
|
||||
/* Unacked frames */
|
||||
hdev->acl_cnt += conn->sent;
|
||||
} else if (conn->type == LE_LINK) {
|
||||
if (hdev->le_pkts)
|
||||
hdev->le_cnt += conn->sent;
|
||||
else
|
||||
hdev->acl_cnt += conn->sent;
|
||||
} else {
|
||||
struct hci_conn *acl = conn->link;
|
||||
if (acl) {
|
||||
@@ -360,15 +417,30 @@ struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src)
|
||||
}
|
||||
EXPORT_SYMBOL(hci_get_route);
|
||||
|
||||
/* Create SCO or ACL connection.
|
||||
/* Create SCO, ACL or LE connection.
|
||||
* Device _must_ be locked */
|
||||
struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 sec_level, __u8 auth_type)
|
||||
{
|
||||
struct hci_conn *acl;
|
||||
struct hci_conn *sco;
|
||||
struct hci_conn *le;
|
||||
|
||||
BT_DBG("%s dst %s", hdev->name, batostr(dst));
|
||||
|
||||
if (type == LE_LINK) {
|
||||
le = hci_conn_hash_lookup_ba(hdev, LE_LINK, dst);
|
||||
if (!le)
|
||||
le = hci_conn_add(hdev, LE_LINK, dst);
|
||||
if (!le)
|
||||
return NULL;
|
||||
if (le->state == BT_OPEN)
|
||||
hci_le_connect(le);
|
||||
|
||||
hci_conn_hold(le);
|
||||
|
||||
return le;
|
||||
}
|
||||
|
||||
acl = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
|
||||
if (!acl) {
|
||||
acl = hci_conn_add(hdev, ACL_LINK, dst);
|
||||
|
||||
+306
-39
@@ -41,6 +41,7 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/rfkill.h>
|
||||
#include <linux/timer.h>
|
||||
#include <net/sock.h>
|
||||
|
||||
#include <asm/system.h>
|
||||
@@ -50,6 +51,8 @@
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
|
||||
#define AUTO_OFF_TIMEOUT 2000
|
||||
|
||||
static void hci_cmd_task(unsigned long arg);
|
||||
static void hci_rx_task(unsigned long arg);
|
||||
static void hci_tx_task(unsigned long arg);
|
||||
@@ -95,11 +98,10 @@ void hci_req_complete(struct hci_dev *hdev, __u16 cmd, int result)
|
||||
{
|
||||
BT_DBG("%s command 0x%04x result 0x%2.2x", hdev->name, cmd, result);
|
||||
|
||||
/* If the request has set req_last_cmd (typical for multi-HCI
|
||||
* command requests) check if the completed command matches
|
||||
* this, and if not just return. Single HCI command requests
|
||||
* typically leave req_last_cmd as 0 */
|
||||
if (hdev->req_last_cmd && cmd != hdev->req_last_cmd)
|
||||
/* If this is the init phase check if the completed command matches
|
||||
* the last init command, and if not just return.
|
||||
*/
|
||||
if (test_bit(HCI_INIT, &hdev->flags) && hdev->init_last_cmd != cmd)
|
||||
return;
|
||||
|
||||
if (hdev->req_status == HCI_REQ_PEND) {
|
||||
@@ -122,7 +124,7 @@ static void hci_req_cancel(struct hci_dev *hdev, int err)
|
||||
|
||||
/* Execute request and wait for completion. */
|
||||
static int __hci_request(struct hci_dev *hdev, void (*req)(struct hci_dev *hdev, unsigned long opt),
|
||||
unsigned long opt, __u32 timeout)
|
||||
unsigned long opt, __u32 timeout)
|
||||
{
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
int err = 0;
|
||||
@@ -156,7 +158,7 @@ static int __hci_request(struct hci_dev *hdev, void (*req)(struct hci_dev *hdev,
|
||||
break;
|
||||
}
|
||||
|
||||
hdev->req_last_cmd = hdev->req_status = hdev->req_result = 0;
|
||||
hdev->req_status = hdev->req_result = 0;
|
||||
|
||||
BT_DBG("%s end: err %d", hdev->name, err);
|
||||
|
||||
@@ -164,7 +166,7 @@ static int __hci_request(struct hci_dev *hdev, void (*req)(struct hci_dev *hdev,
|
||||
}
|
||||
|
||||
static inline int hci_request(struct hci_dev *hdev, void (*req)(struct hci_dev *hdev, unsigned long opt),
|
||||
unsigned long opt, __u32 timeout)
|
||||
unsigned long opt, __u32 timeout)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@@ -189,6 +191,7 @@ static void hci_reset_req(struct hci_dev *hdev, unsigned long opt)
|
||||
|
||||
static void hci_init_req(struct hci_dev *hdev, unsigned long opt)
|
||||
{
|
||||
struct hci_cp_delete_stored_link_key cp;
|
||||
struct sk_buff *skb;
|
||||
__le16 param;
|
||||
__u8 flt_type;
|
||||
@@ -252,15 +255,21 @@ static void hci_init_req(struct hci_dev *hdev, unsigned long opt)
|
||||
flt_type = HCI_FLT_CLEAR_ALL;
|
||||
hci_send_cmd(hdev, HCI_OP_SET_EVENT_FLT, 1, &flt_type);
|
||||
|
||||
/* Page timeout ~20 secs */
|
||||
param = cpu_to_le16(0x8000);
|
||||
hci_send_cmd(hdev, HCI_OP_WRITE_PG_TIMEOUT, 2, ¶m);
|
||||
|
||||
/* Connection accept timeout ~20 secs */
|
||||
param = cpu_to_le16(0x7d00);
|
||||
hci_send_cmd(hdev, HCI_OP_WRITE_CA_TIMEOUT, 2, ¶m);
|
||||
|
||||
hdev->req_last_cmd = HCI_OP_WRITE_CA_TIMEOUT;
|
||||
bacpy(&cp.bdaddr, BDADDR_ANY);
|
||||
cp.delete_all = 1;
|
||||
hci_send_cmd(hdev, HCI_OP_DELETE_STORED_LINK_KEY, sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
static void hci_le_init_req(struct hci_dev *hdev, unsigned long opt)
|
||||
{
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
/* Read LE buffer size */
|
||||
hci_send_cmd(hdev, HCI_OP_LE_READ_BUFFER_SIZE, 0, NULL);
|
||||
}
|
||||
|
||||
static void hci_scan_req(struct hci_dev *hdev, unsigned long opt)
|
||||
@@ -429,7 +438,8 @@ int hci_inquiry(void __user *arg)
|
||||
if (copy_from_user(&ir, ptr, sizeof(ir)))
|
||||
return -EFAULT;
|
||||
|
||||
if (!(hdev = hci_dev_get(ir.dev_id)))
|
||||
hdev = hci_dev_get(ir.dev_id);
|
||||
if (!hdev)
|
||||
return -ENODEV;
|
||||
|
||||
hci_dev_lock_bh(hdev);
|
||||
@@ -455,7 +465,7 @@ int hci_inquiry(void __user *arg)
|
||||
/* cache_dump can't sleep. Therefore we allocate temp buffer and then
|
||||
* copy it to the user space.
|
||||
*/
|
||||
buf = kmalloc(sizeof(struct inquiry_info) *max_rsp, GFP_KERNEL);
|
||||
buf = kmalloc(sizeof(struct inquiry_info) * max_rsp, GFP_KERNEL);
|
||||
if (!buf) {
|
||||
err = -ENOMEM;
|
||||
goto done;
|
||||
@@ -489,7 +499,8 @@ int hci_dev_open(__u16 dev)
|
||||
struct hci_dev *hdev;
|
||||
int ret = 0;
|
||||
|
||||
if (!(hdev = hci_dev_get(dev)))
|
||||
hdev = hci_dev_get(dev);
|
||||
if (!hdev)
|
||||
return -ENODEV;
|
||||
|
||||
BT_DBG("%s %p", hdev->name, hdev);
|
||||
@@ -521,11 +532,15 @@ int hci_dev_open(__u16 dev)
|
||||
if (!test_bit(HCI_RAW, &hdev->flags)) {
|
||||
atomic_set(&hdev->cmd_cnt, 1);
|
||||
set_bit(HCI_INIT, &hdev->flags);
|
||||
hdev->init_last_cmd = 0;
|
||||
|
||||
//__hci_request(hdev, hci_reset_req, 0, HZ);
|
||||
ret = __hci_request(hdev, hci_init_req, 0,
|
||||
msecs_to_jiffies(HCI_INIT_TIMEOUT));
|
||||
|
||||
if (lmp_le_capable(hdev))
|
||||
ret = __hci_request(hdev, hci_le_init_req, 0,
|
||||
msecs_to_jiffies(HCI_INIT_TIMEOUT));
|
||||
|
||||
clear_bit(HCI_INIT, &hdev->flags);
|
||||
}
|
||||
|
||||
@@ -533,6 +548,8 @@ 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);
|
||||
} else {
|
||||
/* Init failed, cleanup */
|
||||
tasklet_kill(&hdev->rx_task);
|
||||
@@ -606,6 +623,7 @@ static int hci_dev_do_close(struct hci_dev *hdev)
|
||||
|
||||
/* Drop last sent command */
|
||||
if (hdev->sent_cmd) {
|
||||
del_timer_sync(&hdev->cmd_timer);
|
||||
kfree_skb(hdev->sent_cmd);
|
||||
hdev->sent_cmd = NULL;
|
||||
}
|
||||
@@ -614,6 +632,8 @@ static int hci_dev_do_close(struct hci_dev *hdev)
|
||||
* and no tasks are scheduled. */
|
||||
hdev->close(hdev);
|
||||
|
||||
mgmt_powered(hdev->id, 0);
|
||||
|
||||
/* Clear flags */
|
||||
hdev->flags = 0;
|
||||
|
||||
@@ -664,7 +684,7 @@ int hci_dev_reset(__u16 dev)
|
||||
hdev->flush(hdev);
|
||||
|
||||
atomic_set(&hdev->cmd_cnt, 1);
|
||||
hdev->acl_cnt = 0; hdev->sco_cnt = 0;
|
||||
hdev->acl_cnt = 0; hdev->sco_cnt = 0; hdev->le_cnt = 0;
|
||||
|
||||
if (!test_bit(HCI_RAW, &hdev->flags))
|
||||
ret = __hci_request(hdev, hci_reset_req, 0,
|
||||
@@ -793,9 +813,17 @@ int hci_get_dev_list(void __user *arg)
|
||||
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);
|
||||
|
||||
if (!test_bit(HCI_MGMT, &hdev->flags))
|
||||
set_bit(HCI_PAIRABLE, &hdev->flags);
|
||||
|
||||
(dr + n)->dev_id = hdev->id;
|
||||
(dr + n)->dev_opt = hdev->flags;
|
||||
|
||||
if (++n >= dev_num)
|
||||
break;
|
||||
}
|
||||
@@ -823,6 +851,11 @@ int hci_get_dev_info(void __user *arg)
|
||||
if (!hdev)
|
||||
return -ENODEV;
|
||||
|
||||
hci_del_off_timer(hdev);
|
||||
|
||||
if (!test_bit(HCI_MGMT, &hdev->flags))
|
||||
set_bit(HCI_PAIRABLE, &hdev->flags);
|
||||
|
||||
strcpy(di.name, hdev->name);
|
||||
di.bdaddr = hdev->bdaddr;
|
||||
di.type = (hdev->bus & 0x0f) | (hdev->dev_type << 4);
|
||||
@@ -891,6 +924,159 @@ void hci_free_dev(struct hci_dev *hdev)
|
||||
}
|
||||
EXPORT_SYMBOL(hci_free_dev);
|
||||
|
||||
static void hci_power_on(struct work_struct *work)
|
||||
{
|
||||
struct hci_dev *hdev = container_of(work, struct hci_dev, power_on);
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
if (hci_dev_open(hdev->id) < 0)
|
||||
return;
|
||||
|
||||
if (test_bit(HCI_AUTO_OFF, &hdev->flags))
|
||||
mod_timer(&hdev->off_timer,
|
||||
jiffies + msecs_to_jiffies(AUTO_OFF_TIMEOUT));
|
||||
|
||||
if (test_and_clear_bit(HCI_SETUP, &hdev->flags))
|
||||
mgmt_index_added(hdev->id);
|
||||
}
|
||||
|
||||
static void hci_power_off(struct work_struct *work)
|
||||
{
|
||||
struct hci_dev *hdev = container_of(work, struct hci_dev, power_off);
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
hci_dev_close(hdev->id);
|
||||
}
|
||||
|
||||
static void hci_auto_off(unsigned long data)
|
||||
{
|
||||
struct hci_dev *hdev = (struct hci_dev *) data;
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
clear_bit(HCI_AUTO_OFF, &hdev->flags);
|
||||
|
||||
queue_work(hdev->workqueue, &hdev->power_off);
|
||||
}
|
||||
|
||||
void hci_del_off_timer(struct hci_dev *hdev)
|
||||
{
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
clear_bit(HCI_AUTO_OFF, &hdev->flags);
|
||||
del_timer(&hdev->off_timer);
|
||||
}
|
||||
|
||||
int hci_uuids_clear(struct hci_dev *hdev)
|
||||
{
|
||||
struct list_head *p, *n;
|
||||
|
||||
list_for_each_safe(p, n, &hdev->uuids) {
|
||||
struct bt_uuid *uuid;
|
||||
|
||||
uuid = list_entry(p, struct bt_uuid, list);
|
||||
|
||||
list_del(p);
|
||||
kfree(uuid);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hci_link_keys_clear(struct hci_dev *hdev)
|
||||
{
|
||||
struct list_head *p, *n;
|
||||
|
||||
list_for_each_safe(p, n, &hdev->link_keys) {
|
||||
struct link_key *key;
|
||||
|
||||
key = list_entry(p, struct link_key, list);
|
||||
|
||||
list_del(p);
|
||||
kfree(key);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (bacmp(bdaddr, &k->bdaddr) == 0)
|
||||
return k;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int hci_add_link_key(struct hci_dev *hdev, int new_key, bdaddr_t *bdaddr,
|
||||
u8 *val, u8 type, u8 pin_len)
|
||||
{
|
||||
struct link_key *key, *old_key;
|
||||
u8 old_key_type;
|
||||
|
||||
old_key = hci_find_link_key(hdev, bdaddr);
|
||||
if (old_key) {
|
||||
old_key_type = old_key->type;
|
||||
key = old_key;
|
||||
} else {
|
||||
old_key_type = 0xff;
|
||||
key = kzalloc(sizeof(*key), GFP_ATOMIC);
|
||||
if (!key)
|
||||
return -ENOMEM;
|
||||
list_add(&key->list, &hdev->link_keys);
|
||||
}
|
||||
|
||||
BT_DBG("%s key for %s type %u", hdev->name, batostr(bdaddr), type);
|
||||
|
||||
bacpy(&key->bdaddr, bdaddr);
|
||||
memcpy(key->val, val, 16);
|
||||
key->type = type;
|
||||
key->pin_len = pin_len;
|
||||
|
||||
if (new_key)
|
||||
mgmt_new_key(hdev->id, key, old_key_type);
|
||||
|
||||
if (type == 0x06)
|
||||
key->type = old_key_type;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr)
|
||||
{
|
||||
struct link_key *key;
|
||||
|
||||
key = hci_find_link_key(hdev, bdaddr);
|
||||
if (!key)
|
||||
return -ENOENT;
|
||||
|
||||
BT_DBG("%s removing %s", hdev->name, batostr(bdaddr));
|
||||
|
||||
list_del(&key->list);
|
||||
kfree(key);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* HCI command timer function */
|
||||
static void hci_cmd_timer(unsigned long arg)
|
||||
{
|
||||
struct hci_dev *hdev = (void *) arg;
|
||||
|
||||
BT_ERR("%s command tx timeout", hdev->name);
|
||||
atomic_set(&hdev->cmd_cnt, 1);
|
||||
tasklet_schedule(&hdev->cmd_task);
|
||||
}
|
||||
|
||||
/* Register HCI device */
|
||||
int hci_register_dev(struct hci_dev *hdev)
|
||||
{
|
||||
@@ -923,6 +1109,7 @@ int hci_register_dev(struct hci_dev *hdev)
|
||||
hdev->pkt_type = (HCI_DM1 | HCI_DH1 | HCI_HV1);
|
||||
hdev->esco_type = (ESCO_HV1);
|
||||
hdev->link_mode = (HCI_LM_ACCEPT);
|
||||
hdev->io_capability = 0x03; /* No Input No Output */
|
||||
|
||||
hdev->idle_timeout = 0;
|
||||
hdev->sniff_max_interval = 800;
|
||||
@@ -936,6 +1123,8 @@ int hci_register_dev(struct hci_dev *hdev)
|
||||
skb_queue_head_init(&hdev->cmd_q);
|
||||
skb_queue_head_init(&hdev->raw_q);
|
||||
|
||||
setup_timer(&hdev->cmd_timer, hci_cmd_timer, (unsigned long) hdev);
|
||||
|
||||
for (i = 0; i < NUM_REASSEMBLY; i++)
|
||||
hdev->reassembly[i] = NULL;
|
||||
|
||||
@@ -948,6 +1137,14 @@ int hci_register_dev(struct hci_dev *hdev)
|
||||
|
||||
INIT_LIST_HEAD(&hdev->blacklist);
|
||||
|
||||
INIT_LIST_HEAD(&hdev->uuids);
|
||||
|
||||
INIT_LIST_HEAD(&hdev->link_keys);
|
||||
|
||||
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);
|
||||
|
||||
memset(&hdev->stat, 0, sizeof(struct hci_dev_stats));
|
||||
|
||||
atomic_set(&hdev->promisc, 0);
|
||||
@@ -969,7 +1166,10 @@ int hci_register_dev(struct hci_dev *hdev)
|
||||
}
|
||||
}
|
||||
|
||||
mgmt_index_added(hdev->id);
|
||||
set_bit(HCI_AUTO_OFF, &hdev->flags);
|
||||
set_bit(HCI_SETUP, &hdev->flags);
|
||||
queue_work(hdev->workqueue, &hdev->power_on);
|
||||
|
||||
hci_notify(hdev, HCI_DEV_REG);
|
||||
|
||||
return id;
|
||||
@@ -999,7 +1199,10 @@ int hci_unregister_dev(struct hci_dev *hdev)
|
||||
for (i = 0; i < NUM_REASSEMBLY; i++)
|
||||
kfree_skb(hdev->reassembly[i]);
|
||||
|
||||
mgmt_index_removed(hdev->id);
|
||||
if (!test_bit(HCI_INIT, &hdev->flags) &&
|
||||
!test_bit(HCI_SETUP, &hdev->flags))
|
||||
mgmt_index_removed(hdev->id);
|
||||
|
||||
hci_notify(hdev, HCI_DEV_UNREG);
|
||||
|
||||
if (hdev->rfkill) {
|
||||
@@ -1009,10 +1212,14 @@ int hci_unregister_dev(struct hci_dev *hdev)
|
||||
|
||||
hci_unregister_sysfs(hdev);
|
||||
|
||||
hci_del_off_timer(hdev);
|
||||
|
||||
destroy_workqueue(hdev->workqueue);
|
||||
|
||||
hci_dev_lock_bh(hdev);
|
||||
hci_blacklist_clear(hdev);
|
||||
hci_uuids_clear(hdev);
|
||||
hci_link_keys_clear(hdev);
|
||||
hci_dev_unlock_bh(hdev);
|
||||
|
||||
__hci_dev_put(hdev);
|
||||
@@ -1313,7 +1520,7 @@ static int hci_send_frame(struct sk_buff *skb)
|
||||
/* Time stamp */
|
||||
__net_timestamp(skb);
|
||||
|
||||
hci_send_to_sock(hdev, skb);
|
||||
hci_send_to_sock(hdev, skb, NULL);
|
||||
}
|
||||
|
||||
/* Get rid of skb owner, prior to sending to the driver. */
|
||||
@@ -1349,6 +1556,9 @@ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param)
|
||||
bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
|
||||
skb->dev = (void *) hdev;
|
||||
|
||||
if (test_bit(HCI_INIT, &hdev->flags))
|
||||
hdev->init_last_cmd = opcode;
|
||||
|
||||
skb_queue_tail(&hdev->cmd_q, skb);
|
||||
tasklet_schedule(&hdev->cmd_task);
|
||||
|
||||
@@ -1395,7 +1605,7 @@ void hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags)
|
||||
|
||||
skb->dev = (void *) hdev;
|
||||
bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT;
|
||||
hci_add_acl_hdr(skb, conn->handle, flags | ACL_START);
|
||||
hci_add_acl_hdr(skb, conn->handle, flags);
|
||||
|
||||
list = skb_shinfo(skb)->frag_list;
|
||||
if (!list) {
|
||||
@@ -1413,12 +1623,15 @@ void hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags)
|
||||
spin_lock_bh(&conn->data_q.lock);
|
||||
|
||||
__skb_queue_tail(&conn->data_q, skb);
|
||||
|
||||
flags &= ~ACL_START;
|
||||
flags |= ACL_CONT;
|
||||
do {
|
||||
skb = list; list = list->next;
|
||||
|
||||
skb->dev = (void *) hdev;
|
||||
bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT;
|
||||
hci_add_acl_hdr(skb, conn->handle, flags | ACL_CONT);
|
||||
hci_add_acl_hdr(skb, conn->handle, flags);
|
||||
|
||||
BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len);
|
||||
|
||||
@@ -1486,8 +1699,25 @@ static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int
|
||||
}
|
||||
|
||||
if (conn) {
|
||||
int cnt = (type == ACL_LINK ? hdev->acl_cnt : hdev->sco_cnt);
|
||||
int q = cnt / num;
|
||||
int cnt, q;
|
||||
|
||||
switch (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;
|
||||
} else
|
||||
*quote = 0;
|
||||
@@ -1496,19 +1726,19 @@ static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int
|
||||
return conn;
|
||||
}
|
||||
|
||||
static inline void hci_acl_tx_to(struct hci_dev *hdev)
|
||||
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;
|
||||
|
||||
BT_ERR("%s ACL tx timeout", hdev->name);
|
||||
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);
|
||||
if (c->type == ACL_LINK && c->sent) {
|
||||
BT_ERR("%s killing stalled ACL connection %s",
|
||||
if (c->type == type && c->sent) {
|
||||
BT_ERR("%s killing stalled connection %s",
|
||||
hdev->name, batostr(&c->dst));
|
||||
hci_acl_disconn(c, 0x13);
|
||||
}
|
||||
@@ -1527,7 +1757,7 @@ static inline void hci_sched_acl(struct hci_dev *hdev)
|
||||
/* ACL tx timeout must be longer than maximum
|
||||
* link supervision timeout (40.9 seconds) */
|
||||
if (!hdev->acl_cnt && time_after(jiffies, hdev->acl_last_tx + HZ * 45))
|
||||
hci_acl_tx_to(hdev);
|
||||
hci_link_tx_to(hdev, ACL_LINK);
|
||||
}
|
||||
|
||||
while (hdev->acl_cnt && (conn = hci_low_sent(hdev, ACL_LINK, "e))) {
|
||||
@@ -1586,6 +1816,40 @@ 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 sk_buff *skb;
|
||||
int quote, cnt;
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
if (!test_bit(HCI_RAW, &hdev->flags)) {
|
||||
/* LE tx timeout must be longer than maximum
|
||||
* link supervision timeout (40.9 seconds) */
|
||||
if (!hdev->le_cnt && hdev->le_pkts &&
|
||||
time_after(jiffies, hdev->le_last_tx + HZ * 45))
|
||||
hci_link_tx_to(hdev, LE_LINK);
|
||||
}
|
||||
|
||||
cnt = hdev->le_pkts ? hdev->le_cnt : hdev->acl_cnt;
|
||||
while (cnt && (conn = hci_low_sent(hdev, LE_LINK, "e))) {
|
||||
while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
|
||||
BT_DBG("skb %p len %d", skb, skb->len);
|
||||
|
||||
hci_send_frame(skb);
|
||||
hdev->le_last_tx = jiffies;
|
||||
|
||||
cnt--;
|
||||
conn->sent++;
|
||||
}
|
||||
}
|
||||
if (hdev->le_pkts)
|
||||
hdev->le_cnt = cnt;
|
||||
else
|
||||
hdev->acl_cnt = cnt;
|
||||
}
|
||||
|
||||
static void hci_tx_task(unsigned long arg)
|
||||
{
|
||||
struct hci_dev *hdev = (struct hci_dev *) arg;
|
||||
@@ -1593,7 +1857,8 @@ static void hci_tx_task(unsigned long arg)
|
||||
|
||||
read_lock(&hci_task_lock);
|
||||
|
||||
BT_DBG("%s acl %d sco %d", hdev->name, hdev->acl_cnt, hdev->sco_cnt);
|
||||
BT_DBG("%s acl %d sco %d le %d", hdev->name, hdev->acl_cnt,
|
||||
hdev->sco_cnt, hdev->le_cnt);
|
||||
|
||||
/* Schedule queues and send stuff to HCI driver */
|
||||
|
||||
@@ -1603,6 +1868,8 @@ static void hci_tx_task(unsigned long arg)
|
||||
|
||||
hci_sched_esco(hdev);
|
||||
|
||||
hci_sched_le(hdev);
|
||||
|
||||
/* Send next queued raw (unknown type) packet */
|
||||
while ((skb = skb_dequeue(&hdev->raw_q)))
|
||||
hci_send_frame(skb);
|
||||
@@ -1700,7 +1967,7 @@ static void hci_rx_task(unsigned long arg)
|
||||
while ((skb = skb_dequeue(&hdev->rx_q))) {
|
||||
if (atomic_read(&hdev->promisc)) {
|
||||
/* Send copy to the sockets */
|
||||
hci_send_to_sock(hdev, skb);
|
||||
hci_send_to_sock(hdev, skb, NULL);
|
||||
}
|
||||
|
||||
if (test_bit(HCI_RAW, &hdev->flags)) {
|
||||
@@ -1750,20 +2017,20 @@ static void hci_cmd_task(unsigned long arg)
|
||||
|
||||
BT_DBG("%s cmd %d", hdev->name, atomic_read(&hdev->cmd_cnt));
|
||||
|
||||
if (!atomic_read(&hdev->cmd_cnt) && time_after(jiffies, hdev->cmd_last_tx + HZ)) {
|
||||
BT_ERR("%s command tx timeout", hdev->name);
|
||||
atomic_set(&hdev->cmd_cnt, 1);
|
||||
}
|
||||
|
||||
/* Send queued commands */
|
||||
if (atomic_read(&hdev->cmd_cnt) && (skb = skb_dequeue(&hdev->cmd_q))) {
|
||||
if (atomic_read(&hdev->cmd_cnt)) {
|
||||
skb = skb_dequeue(&hdev->cmd_q);
|
||||
if (!skb)
|
||||
return;
|
||||
|
||||
kfree_skb(hdev->sent_cmd);
|
||||
|
||||
hdev->sent_cmd = skb_clone(skb, GFP_ATOMIC);
|
||||
if (hdev->sent_cmd) {
|
||||
atomic_dec(&hdev->cmd_cnt);
|
||||
hci_send_frame(skb);
|
||||
hdev->cmd_last_tx = jiffies;
|
||||
mod_timer(&hdev->cmd_timer,
|
||||
jiffies + msecs_to_jiffies(HCI_CMD_TIMEOUT));
|
||||
} else {
|
||||
skb_queue_head(&hdev->cmd_q, skb);
|
||||
tasklet_schedule(&hdev->cmd_task);
|
||||
|
||||
+582
-40
@@ -274,15 +274,24 @@ static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
|
||||
if (!status) {
|
||||
__u8 param = *((__u8 *) sent);
|
||||
int old_pscan, old_iscan;
|
||||
|
||||
clear_bit(HCI_PSCAN, &hdev->flags);
|
||||
clear_bit(HCI_ISCAN, &hdev->flags);
|
||||
old_pscan = test_and_clear_bit(HCI_PSCAN, &hdev->flags);
|
||||
old_iscan = test_and_clear_bit(HCI_ISCAN, &hdev->flags);
|
||||
|
||||
if (param & SCAN_INQUIRY)
|
||||
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);
|
||||
|
||||
if (param & SCAN_PAGE)
|
||||
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);
|
||||
}
|
||||
|
||||
hci_req_complete(hdev, HCI_OP_WRITE_SCAN_ENABLE, status);
|
||||
@@ -415,6 +424,115 @@ static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
hdev->ssp_mode = *((__u8 *) sent);
|
||||
}
|
||||
|
||||
static u8 hci_get_inquiry_mode(struct hci_dev *hdev)
|
||||
{
|
||||
if (hdev->features[6] & LMP_EXT_INQ)
|
||||
return 2;
|
||||
|
||||
if (hdev->features[3] & LMP_RSSI_INQ)
|
||||
return 1;
|
||||
|
||||
if (hdev->manufacturer == 11 && hdev->hci_rev == 0x00 &&
|
||||
hdev->lmp_subver == 0x0757)
|
||||
return 1;
|
||||
|
||||
if (hdev->manufacturer == 15) {
|
||||
if (hdev->hci_rev == 0x03 && hdev->lmp_subver == 0x6963)
|
||||
return 1;
|
||||
if (hdev->hci_rev == 0x09 && hdev->lmp_subver == 0x6963)
|
||||
return 1;
|
||||
if (hdev->hci_rev == 0x00 && hdev->lmp_subver == 0x6965)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (hdev->manufacturer == 31 && hdev->hci_rev == 0x2005 &&
|
||||
hdev->lmp_subver == 0x1805)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hci_setup_inquiry_mode(struct hci_dev *hdev)
|
||||
{
|
||||
u8 mode;
|
||||
|
||||
mode = hci_get_inquiry_mode(hdev);
|
||||
|
||||
hci_send_cmd(hdev, HCI_OP_WRITE_INQUIRY_MODE, 1, &mode);
|
||||
}
|
||||
|
||||
static void hci_setup_event_mask(struct hci_dev *hdev)
|
||||
{
|
||||
/* The second byte is 0xff instead of 0x9f (two reserved bits
|
||||
* disabled) since a Broadcom 1.2 dongle doesn't respond to the
|
||||
* command otherwise */
|
||||
u8 events[8] = { 0xff, 0xff, 0xfb, 0xff, 0x00, 0x00, 0x00, 0x00 };
|
||||
|
||||
/* Events for 1.2 and newer controllers */
|
||||
if (hdev->lmp_ver > 1) {
|
||||
events[4] |= 0x01; /* Flow Specification Complete */
|
||||
events[4] |= 0x02; /* Inquiry Result with RSSI */
|
||||
events[4] |= 0x04; /* Read Remote Extended Features Complete */
|
||||
events[5] |= 0x08; /* Synchronous Connection Complete */
|
||||
events[5] |= 0x10; /* Synchronous Connection Changed */
|
||||
}
|
||||
|
||||
if (hdev->features[3] & LMP_RSSI_INQ)
|
||||
events[4] |= 0x04; /* Inquiry Result with RSSI */
|
||||
|
||||
if (hdev->features[5] & LMP_SNIFF_SUBR)
|
||||
events[5] |= 0x20; /* Sniff Subrating */
|
||||
|
||||
if (hdev->features[5] & LMP_PAUSE_ENC)
|
||||
events[5] |= 0x80; /* Encryption Key Refresh Complete */
|
||||
|
||||
if (hdev->features[6] & LMP_EXT_INQ)
|
||||
events[5] |= 0x40; /* Extended Inquiry Result */
|
||||
|
||||
if (hdev->features[6] & LMP_NO_FLUSH)
|
||||
events[7] |= 0x01; /* Enhanced Flush Complete */
|
||||
|
||||
if (hdev->features[7] & LMP_LSTO)
|
||||
events[6] |= 0x80; /* Link Supervision Timeout Changed */
|
||||
|
||||
if (hdev->features[6] & LMP_SIMPLE_PAIR) {
|
||||
events[6] |= 0x01; /* IO Capability Request */
|
||||
events[6] |= 0x02; /* IO Capability Response */
|
||||
events[6] |= 0x04; /* User Confirmation Request */
|
||||
events[6] |= 0x08; /* User Passkey Request */
|
||||
events[6] |= 0x10; /* Remote OOB Data Request */
|
||||
events[6] |= 0x20; /* Simple Pairing Complete */
|
||||
events[7] |= 0x04; /* User Passkey Notification */
|
||||
events[7] |= 0x08; /* Keypress Notification */
|
||||
events[7] |= 0x10; /* Remote Host Supported
|
||||
* Features Notification */
|
||||
}
|
||||
|
||||
if (hdev->features[4] & LMP_LE)
|
||||
events[7] |= 0x20; /* LE Meta-Event */
|
||||
|
||||
hci_send_cmd(hdev, HCI_OP_SET_EVENT_MASK, sizeof(events), events);
|
||||
}
|
||||
|
||||
static void hci_setup(struct hci_dev *hdev)
|
||||
{
|
||||
hci_setup_event_mask(hdev);
|
||||
|
||||
if (hdev->lmp_ver > 1)
|
||||
hci_send_cmd(hdev, HCI_OP_READ_LOCAL_COMMANDS, 0, NULL);
|
||||
|
||||
if (hdev->features[6] & LMP_SIMPLE_PAIR) {
|
||||
u8 mode = 0x01;
|
||||
hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, sizeof(mode), &mode);
|
||||
}
|
||||
|
||||
if (hdev->features[3] & LMP_RSSI_INQ)
|
||||
hci_setup_inquiry_mode(hdev);
|
||||
|
||||
if (hdev->features[7] & LMP_INQ_TX_PWR)
|
||||
hci_send_cmd(hdev, HCI_OP_READ_INQ_RSP_TX_POWER, 0, NULL);
|
||||
}
|
||||
|
||||
static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_rp_read_local_version *rp = (void *) skb->data;
|
||||
@@ -426,11 +544,34 @@ static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
|
||||
hdev->hci_ver = rp->hci_ver;
|
||||
hdev->hci_rev = __le16_to_cpu(rp->hci_rev);
|
||||
hdev->lmp_ver = rp->lmp_ver;
|
||||
hdev->manufacturer = __le16_to_cpu(rp->manufacturer);
|
||||
hdev->lmp_subver = __le16_to_cpu(rp->lmp_subver);
|
||||
|
||||
BT_DBG("%s manufacturer %d hci ver %d:%d", hdev->name,
|
||||
hdev->manufacturer,
|
||||
hdev->hci_ver, hdev->hci_rev);
|
||||
|
||||
if (test_bit(HCI_INIT, &hdev->flags))
|
||||
hci_setup(hdev);
|
||||
}
|
||||
|
||||
static void hci_setup_link_policy(struct hci_dev *hdev)
|
||||
{
|
||||
u16 link_policy = 0;
|
||||
|
||||
if (hdev->features[0] & LMP_RSWITCH)
|
||||
link_policy |= HCI_LP_RSWITCH;
|
||||
if (hdev->features[0] & LMP_HOLD)
|
||||
link_policy |= HCI_LP_HOLD;
|
||||
if (hdev->features[0] & LMP_SNIFF)
|
||||
link_policy |= HCI_LP_SNIFF;
|
||||
if (hdev->features[1] & LMP_PARK)
|
||||
link_policy |= HCI_LP_PARK;
|
||||
|
||||
link_policy = cpu_to_le16(link_policy);
|
||||
hci_send_cmd(hdev, HCI_OP_WRITE_DEF_LINK_POLICY,
|
||||
sizeof(link_policy), &link_policy);
|
||||
}
|
||||
|
||||
static void hci_cc_read_local_commands(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
@@ -440,9 +581,15 @@ static void hci_cc_read_local_commands(struct hci_dev *hdev, struct sk_buff *skb
|
||||
BT_DBG("%s status 0x%x", hdev->name, rp->status);
|
||||
|
||||
if (rp->status)
|
||||
return;
|
||||
goto done;
|
||||
|
||||
memcpy(hdev->commands, rp->commands, sizeof(hdev->commands));
|
||||
|
||||
if (test_bit(HCI_INIT, &hdev->flags) && (hdev->commands[5] & 0x10))
|
||||
hci_setup_link_policy(hdev);
|
||||
|
||||
done:
|
||||
hci_req_complete(hdev, HCI_OP_READ_LOCAL_COMMANDS, rp->status);
|
||||
}
|
||||
|
||||
static void hci_cc_read_local_features(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
@@ -548,6 +695,107 @@ 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_delete_stored_link_key(struct hci_dev *hdev,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
__u8 status = *((__u8 *) skb->data);
|
||||
|
||||
BT_DBG("%s status 0x%x", hdev->name, status);
|
||||
|
||||
hci_req_complete(hdev, HCI_OP_DELETE_STORED_LINK_KEY, status);
|
||||
}
|
||||
|
||||
static void hci_cc_set_event_mask(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
__u8 status = *((__u8 *) skb->data);
|
||||
|
||||
BT_DBG("%s status 0x%x", hdev->name, status);
|
||||
|
||||
hci_req_complete(hdev, HCI_OP_SET_EVENT_MASK, status);
|
||||
}
|
||||
|
||||
static void hci_cc_write_inquiry_mode(struct hci_dev *hdev,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
__u8 status = *((__u8 *) skb->data);
|
||||
|
||||
BT_DBG("%s status 0x%x", hdev->name, status);
|
||||
|
||||
hci_req_complete(hdev, HCI_OP_WRITE_INQUIRY_MODE, status);
|
||||
}
|
||||
|
||||
static void hci_cc_read_inq_rsp_tx_power(struct hci_dev *hdev,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
__u8 status = *((__u8 *) skb->data);
|
||||
|
||||
BT_DBG("%s status 0x%x", hdev->name, status);
|
||||
|
||||
hci_req_complete(hdev, HCI_OP_READ_INQ_RSP_TX_POWER, status);
|
||||
}
|
||||
|
||||
static void hci_cc_set_event_flt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
__u8 status = *((__u8 *) skb->data);
|
||||
|
||||
BT_DBG("%s status 0x%x", hdev->name, status);
|
||||
|
||||
hci_req_complete(hdev, HCI_OP_SET_EVENT_FLT, status);
|
||||
}
|
||||
|
||||
static void hci_cc_pin_code_reply(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_rp_pin_code_reply *rp = (void *) skb->data;
|
||||
struct hci_cp_pin_code_reply *cp;
|
||||
struct hci_conn *conn;
|
||||
|
||||
BT_DBG("%s status 0x%x", hdev->name, rp->status);
|
||||
|
||||
if (test_bit(HCI_MGMT, &hdev->flags))
|
||||
mgmt_pin_code_reply_complete(hdev->id, &rp->bdaddr, rp->status);
|
||||
|
||||
if (rp->status != 0)
|
||||
return;
|
||||
|
||||
cp = hci_sent_cmd_data(hdev, HCI_OP_PIN_CODE_REPLY);
|
||||
if (!cp)
|
||||
return;
|
||||
|
||||
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr);
|
||||
if (conn)
|
||||
conn->pin_length = cp->pin_len;
|
||||
}
|
||||
|
||||
static void hci_cc_pin_code_neg_reply(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_rp_pin_code_neg_reply *rp = (void *) skb->data;
|
||||
|
||||
BT_DBG("%s status 0x%x", hdev->name, rp->status);
|
||||
|
||||
if (test_bit(HCI_MGMT, &hdev->flags))
|
||||
mgmt_pin_code_neg_reply_complete(hdev->id, &rp->bdaddr,
|
||||
rp->status);
|
||||
}
|
||||
static void hci_cc_le_read_buffer_size(struct hci_dev *hdev,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_rp_le_read_buffer_size *rp = (void *) skb->data;
|
||||
|
||||
BT_DBG("%s status 0x%x", hdev->name, rp->status);
|
||||
|
||||
if (rp->status)
|
||||
return;
|
||||
|
||||
hdev->le_mtu = __le16_to_cpu(rp->le_mtu);
|
||||
hdev->le_pkts = rp->le_max_pkt;
|
||||
|
||||
hdev->le_cnt = hdev->le_pkts;
|
||||
|
||||
BT_DBG("%s le mtu %d:%d", hdev->name, hdev->le_mtu, hdev->le_pkts);
|
||||
|
||||
hci_req_complete(hdev, HCI_OP_LE_READ_BUFFER_SIZE, rp->status);
|
||||
}
|
||||
|
||||
static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
|
||||
{
|
||||
BT_DBG("%s status 0x%x", hdev->name, status);
|
||||
@@ -622,11 +870,14 @@ static void hci_cs_add_sco(struct hci_dev *hdev, __u8 status)
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
acl = hci_conn_hash_lookup_handle(hdev, handle);
|
||||
if (acl && (sco = acl->link)) {
|
||||
sco->state = BT_CLOSED;
|
||||
if (acl) {
|
||||
sco = acl->link;
|
||||
if (sco) {
|
||||
sco->state = BT_CLOSED;
|
||||
|
||||
hci_proto_connect_cfm(sco, status);
|
||||
hci_conn_del(sco);
|
||||
hci_proto_connect_cfm(sco, status);
|
||||
hci_conn_del(sco);
|
||||
}
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
@@ -687,7 +938,7 @@ static void hci_cs_set_conn_encrypt(struct hci_dev *hdev, __u8 status)
|
||||
}
|
||||
|
||||
static int hci_outgoing_auth_needed(struct hci_dev *hdev,
|
||||
struct hci_conn *conn)
|
||||
struct hci_conn *conn)
|
||||
{
|
||||
if (conn->state != BT_CONFIG || !conn->out)
|
||||
return 0;
|
||||
@@ -808,11 +1059,14 @@ static void hci_cs_setup_sync_conn(struct hci_dev *hdev, __u8 status)
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
acl = hci_conn_hash_lookup_handle(hdev, handle);
|
||||
if (acl && (sco = acl->link)) {
|
||||
sco->state = BT_CLOSED;
|
||||
if (acl) {
|
||||
sco = acl->link;
|
||||
if (sco) {
|
||||
sco->state = BT_CLOSED;
|
||||
|
||||
hci_proto_connect_cfm(sco, status);
|
||||
hci_conn_del(sco);
|
||||
hci_proto_connect_cfm(sco, status);
|
||||
hci_conn_del(sco);
|
||||
}
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
@@ -872,6 +1126,43 @@ static void hci_cs_exit_sniff_mode(struct hci_dev *hdev, __u8 status)
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static void hci_cs_le_create_conn(struct hci_dev *hdev, __u8 status)
|
||||
{
|
||||
struct hci_cp_le_create_conn *cp;
|
||||
struct hci_conn *conn;
|
||||
|
||||
BT_DBG("%s status 0x%x", hdev->name, status);
|
||||
|
||||
cp = hci_sent_cmd_data(hdev, HCI_OP_LE_CREATE_CONN);
|
||||
if (!cp)
|
||||
return;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->peer_addr);
|
||||
|
||||
BT_DBG("%s bdaddr %s conn %p", hdev->name, batostr(&cp->peer_addr),
|
||||
conn);
|
||||
|
||||
if (status) {
|
||||
if (conn && conn->state == BT_CONNECT) {
|
||||
conn->state = BT_CLOSED;
|
||||
hci_proto_connect_cfm(conn, status);
|
||||
hci_conn_del(conn);
|
||||
}
|
||||
} else {
|
||||
if (!conn) {
|
||||
conn = hci_conn_add(hdev, LE_LINK, &cp->peer_addr);
|
||||
if (conn)
|
||||
conn->out = 1;
|
||||
else
|
||||
BT_ERR("No memory for new connection");
|
||||
}
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
__u8 status = *((__u8 *) skb->data);
|
||||
@@ -942,6 +1233,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);
|
||||
} else
|
||||
conn->state = BT_CONNECTED;
|
||||
|
||||
@@ -970,8 +1262,11 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
|
||||
hci_send_cmd(hdev, HCI_OP_CHANGE_CONN_PTYPE,
|
||||
sizeof(cp), &cp);
|
||||
}
|
||||
} else
|
||||
} else {
|
||||
conn->state = BT_CLOSED;
|
||||
if (conn->type == ACL_LINK)
|
||||
mgmt_connect_failed(hdev->id, &ev->bdaddr, ev->status);
|
||||
}
|
||||
|
||||
if (conn->type == ACL_LINK)
|
||||
hci_sco_setup(conn, ev->status);
|
||||
@@ -998,7 +1293,8 @@ static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *sk
|
||||
|
||||
mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, ev->link_type);
|
||||
|
||||
if ((mask & HCI_LM_ACCEPT) && !hci_blacklist_lookup(hdev, &ev->bdaddr)) {
|
||||
if ((mask & HCI_LM_ACCEPT) &&
|
||||
!hci_blacklist_lookup(hdev, &ev->bdaddr)) {
|
||||
/* Connection accepted */
|
||||
struct inquiry_entry *ie;
|
||||
struct hci_conn *conn;
|
||||
@@ -1068,19 +1364,26 @@ 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)
|
||||
if (ev->status) {
|
||||
mgmt_disconnect_failed(hdev->id);
|
||||
return;
|
||||
}
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
|
||||
if (conn) {
|
||||
conn->state = BT_CLOSED;
|
||||
if (!conn)
|
||||
goto unlock;
|
||||
|
||||
hci_proto_disconn_cfm(conn, ev->reason);
|
||||
hci_conn_del(conn);
|
||||
}
|
||||
conn->state = BT_CLOSED;
|
||||
|
||||
if (conn->type == ACL_LINK)
|
||||
mgmt_disconnected(hdev->id, &conn->dst);
|
||||
|
||||
hci_proto_disconn_cfm(conn, ev->reason);
|
||||
hci_conn_del(conn);
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
@@ -1393,11 +1696,46 @@ 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_DELETE_STORED_LINK_KEY:
|
||||
hci_cc_delete_stored_link_key(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_OP_SET_EVENT_MASK:
|
||||
hci_cc_set_event_mask(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_OP_WRITE_INQUIRY_MODE:
|
||||
hci_cc_write_inquiry_mode(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_OP_READ_INQ_RSP_TX_POWER:
|
||||
hci_cc_read_inq_rsp_tx_power(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_OP_SET_EVENT_FLT:
|
||||
hci_cc_set_event_flt(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_OP_PIN_CODE_REPLY:
|
||||
hci_cc_pin_code_reply(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_OP_PIN_CODE_NEG_REPLY:
|
||||
hci_cc_pin_code_neg_reply(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_OP_LE_READ_BUFFER_SIZE:
|
||||
hci_cc_le_read_buffer_size(hdev, skb);
|
||||
break;
|
||||
|
||||
default:
|
||||
BT_DBG("%s opcode 0x%x", hdev->name, opcode);
|
||||
break;
|
||||
}
|
||||
|
||||
if (ev->opcode != HCI_OP_NOP)
|
||||
del_timer(&hdev->cmd_timer);
|
||||
|
||||
if (ev->ncmd) {
|
||||
atomic_set(&hdev->cmd_cnt, 1);
|
||||
if (!skb_queue_empty(&hdev->cmd_q))
|
||||
@@ -1459,11 +1797,23 @@ static inline void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
hci_cs_exit_sniff_mode(hdev, ev->status);
|
||||
break;
|
||||
|
||||
case HCI_OP_DISCONNECT:
|
||||
if (ev->status != 0)
|
||||
mgmt_disconnect_failed(hdev->id);
|
||||
break;
|
||||
|
||||
case HCI_OP_LE_CREATE_CONN:
|
||||
hci_cs_le_create_conn(hdev, ev->status);
|
||||
break;
|
||||
|
||||
default:
|
||||
BT_DBG("%s opcode 0x%x", hdev->name, opcode);
|
||||
break;
|
||||
}
|
||||
|
||||
if (ev->opcode != HCI_OP_NOP)
|
||||
del_timer(&hdev->cmd_timer);
|
||||
|
||||
if (ev->ncmd) {
|
||||
atomic_set(&hdev->cmd_cnt, 1);
|
||||
if (!skb_queue_empty(&hdev->cmd_q))
|
||||
@@ -1529,6 +1879,16 @@ static inline void hci_num_comp_pkts_evt(struct hci_dev *hdev, struct sk_buff *s
|
||||
hdev->acl_cnt += count;
|
||||
if (hdev->acl_cnt > hdev->acl_pkts)
|
||||
hdev->acl_cnt = hdev->acl_pkts;
|
||||
} else if (conn->type == LE_LINK) {
|
||||
if (hdev->le_pkts) {
|
||||
hdev->le_cnt += count;
|
||||
if (hdev->le_cnt > hdev->le_pkts)
|
||||
hdev->le_cnt = hdev->le_pkts;
|
||||
} else {
|
||||
hdev->acl_cnt += count;
|
||||
if (hdev->acl_cnt > hdev->acl_pkts)
|
||||
hdev->acl_cnt = hdev->acl_pkts;
|
||||
}
|
||||
} else {
|
||||
hdev->sco_cnt += count;
|
||||
if (hdev->sco_cnt > hdev->sco_pkts)
|
||||
@@ -1586,18 +1946,72 @@ static inline void hci_pin_code_request_evt(struct hci_dev *hdev, struct sk_buff
|
||||
hci_conn_put(conn);
|
||||
}
|
||||
|
||||
if (!test_bit(HCI_PAIRABLE, &hdev->flags))
|
||||
hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY,
|
||||
sizeof(ev->bdaddr), &ev->bdaddr);
|
||||
|
||||
if (test_bit(HCI_MGMT, &hdev->flags))
|
||||
mgmt_pin_code_request(hdev->id, &ev->bdaddr);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static inline void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_ev_link_key_req *ev = (void *) skb->data;
|
||||
struct hci_cp_link_key_reply cp;
|
||||
struct hci_conn *conn;
|
||||
struct link_key *key;
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
if (!test_bit(HCI_LINK_KEYS, &hdev->flags))
|
||||
return;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
key = hci_find_link_key(hdev, &ev->bdaddr);
|
||||
if (!key) {
|
||||
BT_DBG("%s link key not found for %s", hdev->name,
|
||||
batostr(&ev->bdaddr));
|
||||
goto not_found;
|
||||
}
|
||||
|
||||
BT_DBG("%s found key type %u for %s", hdev->name, key->type,
|
||||
batostr(&ev->bdaddr));
|
||||
|
||||
if (!test_bit(HCI_DEBUG_KEYS, &hdev->flags) && key->type == 0x03) {
|
||||
BT_DBG("%s ignoring debug key", hdev->name);
|
||||
goto not_found;
|
||||
}
|
||||
|
||||
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
|
||||
|
||||
if (key->type == 0x04 && conn && conn->auth_type != 0xff &&
|
||||
(conn->auth_type & 0x01)) {
|
||||
BT_DBG("%s ignoring unauthenticated key", hdev->name);
|
||||
goto not_found;
|
||||
}
|
||||
|
||||
bacpy(&cp.bdaddr, &ev->bdaddr);
|
||||
memcpy(cp.link_key, key->val, 16);
|
||||
|
||||
hci_send_cmd(hdev, HCI_OP_LINK_KEY_REPLY, sizeof(cp), &cp);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
return;
|
||||
|
||||
not_found:
|
||||
hci_send_cmd(hdev, HCI_OP_LINK_KEY_NEG_REPLY, 6, &ev->bdaddr);
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static inline void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_ev_link_key_notify *ev = (void *) skb->data;
|
||||
struct hci_conn *conn;
|
||||
u8 pin_len = 0;
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
@@ -1607,9 +2021,14 @@ static inline void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff
|
||||
if (conn) {
|
||||
hci_conn_hold(conn);
|
||||
conn->disc_timeout = HCI_DISCONN_TIMEOUT;
|
||||
pin_len = conn->pin_length;
|
||||
hci_conn_put(conn);
|
||||
}
|
||||
|
||||
if (test_bit(HCI_LINK_KEYS, &hdev->flags))
|
||||
hci_add_link_key(hdev, 1, &ev->bdaddr, ev->link_key,
|
||||
ev->key_type, pin_len);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
@@ -1683,7 +2102,8 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if ((skb->len - 1) / num_rsp != sizeof(struct inquiry_info_with_rssi)) {
|
||||
struct inquiry_info_with_rssi_and_pscan_mode *info = (void *) (skb->data + 1);
|
||||
struct inquiry_info_with_rssi_and_pscan_mode *info;
|
||||
info = (void *) (skb->data + 1);
|
||||
|
||||
for (; num_rsp; num_rsp--) {
|
||||
bacpy(&data.bdaddr, &info->bdaddr);
|
||||
@@ -1824,17 +2244,8 @@ static inline void hci_sync_conn_changed_evt(struct hci_dev *hdev, struct sk_buf
|
||||
static inline void hci_sniff_subrate_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_ev_sniff_subrate *ev = (void *) skb->data;
|
||||
struct hci_conn *conn;
|
||||
|
||||
BT_DBG("%s status %d", hdev->name, ev->status);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
|
||||
if (conn) {
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
@@ -1852,12 +2263,12 @@ static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct
|
||||
|
||||
for (; num_rsp; num_rsp--) {
|
||||
bacpy(&data.bdaddr, &info->bdaddr);
|
||||
data.pscan_rep_mode = info->pscan_rep_mode;
|
||||
data.pscan_period_mode = info->pscan_period_mode;
|
||||
data.pscan_mode = 0x00;
|
||||
data.pscan_rep_mode = info->pscan_rep_mode;
|
||||
data.pscan_period_mode = info->pscan_period_mode;
|
||||
data.pscan_mode = 0x00;
|
||||
memcpy(data.dev_class, info->dev_class, 3);
|
||||
data.clock_offset = info->clock_offset;
|
||||
data.rssi = info->rssi;
|
||||
data.clock_offset = info->clock_offset;
|
||||
data.rssi = info->rssi;
|
||||
data.ssp_mode = 0x01;
|
||||
info++;
|
||||
hci_inquiry_cache_update(hdev, &data);
|
||||
@@ -1866,6 +2277,25 @@ static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static inline u8 hci_get_auth_req(struct hci_conn *conn)
|
||||
{
|
||||
/* If remote requests dedicated bonding follow that lead */
|
||||
if (conn->remote_auth == 0x02 || conn->remote_auth == 0x03) {
|
||||
/* If both remote and local IO capabilities allow MITM
|
||||
* protection then require it, otherwise don't */
|
||||
if (conn->remote_cap == 0x03 || conn->io_capability == 0x03)
|
||||
return 0x02;
|
||||
else
|
||||
return 0x03;
|
||||
}
|
||||
|
||||
/* If remote requests no-bonding follow that lead */
|
||||
if (conn->remote_auth == 0x00 || conn->remote_auth == 0x01)
|
||||
return 0x00;
|
||||
|
||||
return conn->auth_type;
|
||||
}
|
||||
|
||||
static inline void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_ev_io_capa_request *ev = (void *) skb->data;
|
||||
@@ -1876,9 +2306,59 @@ static inline void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
|
||||
if (conn)
|
||||
hci_conn_hold(conn);
|
||||
if (!conn)
|
||||
goto unlock;
|
||||
|
||||
hci_conn_hold(conn);
|
||||
|
||||
if (!test_bit(HCI_MGMT, &hdev->flags))
|
||||
goto unlock;
|
||||
|
||||
if (test_bit(HCI_PAIRABLE, &hdev->flags) ||
|
||||
(conn->remote_auth & ~0x01) == HCI_AT_NO_BONDING) {
|
||||
struct hci_cp_io_capability_reply cp;
|
||||
|
||||
bacpy(&cp.bdaddr, &ev->bdaddr);
|
||||
cp.capability = conn->io_capability;
|
||||
cp.oob_data = 0;
|
||||
cp.authentication = hci_get_auth_req(conn);
|
||||
|
||||
hci_send_cmd(hdev, HCI_OP_IO_CAPABILITY_REPLY,
|
||||
sizeof(cp), &cp);
|
||||
} else {
|
||||
struct hci_cp_io_capability_neg_reply cp;
|
||||
|
||||
bacpy(&cp.bdaddr, &ev->bdaddr);
|
||||
cp.reason = 0x16; /* Pairing not allowed */
|
||||
|
||||
hci_send_cmd(hdev, HCI_OP_IO_CAPABILITY_NEG_REPLY,
|
||||
sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static inline void hci_io_capa_reply_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_ev_io_capa_reply *ev = (void *) skb->data;
|
||||
struct hci_conn *conn;
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
|
||||
if (!conn)
|
||||
goto unlock;
|
||||
|
||||
hci_conn_hold(conn);
|
||||
|
||||
conn->remote_cap = ev->capability;
|
||||
conn->remote_oob = ev->oob_data;
|
||||
conn->remote_auth = ev->authentication;
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
@@ -1914,6 +2394,60 @@ static inline void hci_remote_host_features_evt(struct hci_dev *hdev, struct sk_
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_ev_le_conn_complete *ev = (void *) skb->data;
|
||||
struct hci_conn *conn;
|
||||
|
||||
BT_DBG("%s status %d", hdev->name, ev->status);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &ev->bdaddr);
|
||||
if (!conn) {
|
||||
conn = hci_conn_add(hdev, LE_LINK, &ev->bdaddr);
|
||||
if (!conn) {
|
||||
BT_ERR("No memory for new connection");
|
||||
hci_dev_unlock(hdev);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (ev->status) {
|
||||
hci_proto_connect_cfm(conn, ev->status);
|
||||
conn->state = BT_CLOSED;
|
||||
hci_conn_del(conn);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
conn->handle = __le16_to_cpu(ev->handle);
|
||||
conn->state = BT_CONNECTED;
|
||||
|
||||
hci_conn_hold_device(conn);
|
||||
hci_conn_add_sysfs(conn);
|
||||
|
||||
hci_proto_connect_cfm(conn, ev->status);
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static inline void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_ev_le_meta *le_ev = (void *) skb->data;
|
||||
|
||||
skb_pull(skb, sizeof(*le_ev));
|
||||
|
||||
switch (le_ev->subevent) {
|
||||
case HCI_EV_LE_CONN_COMPLETE:
|
||||
hci_le_conn_complete_evt(hdev, skb);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_event_hdr *hdr = (void *) skb->data;
|
||||
@@ -2042,6 +2576,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
hci_io_capa_request_evt(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_EV_IO_CAPA_REPLY:
|
||||
hci_io_capa_reply_evt(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_EV_SIMPLE_PAIR_COMPLETE:
|
||||
hci_simple_pair_complete_evt(hdev, skb);
|
||||
break;
|
||||
@@ -2050,6 +2588,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
hci_remote_host_features_evt(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_EV_LE_META:
|
||||
hci_le_meta_evt(hdev, skb);
|
||||
break;
|
||||
|
||||
default:
|
||||
BT_DBG("%s event 0x%x", hdev->name, event);
|
||||
break;
|
||||
@@ -2083,6 +2625,6 @@ void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data)
|
||||
|
||||
bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
|
||||
skb->dev = (void *) hdev;
|
||||
hci_send_to_sock(hdev, skb);
|
||||
hci_send_to_sock(hdev, skb, NULL);
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
@@ -85,7 +85,8 @@ static struct bt_sock_list hci_sk_list = {
|
||||
};
|
||||
|
||||
/* Send frame to RAW socket */
|
||||
void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb,
|
||||
struct sock *skip_sk)
|
||||
{
|
||||
struct sock *sk;
|
||||
struct hlist_node *node;
|
||||
@@ -97,6 +98,9 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
struct hci_filter *flt;
|
||||
struct sk_buff *nskb;
|
||||
|
||||
if (sk == skip_sk)
|
||||
continue;
|
||||
|
||||
if (sk->sk_state != BT_BOUND || hci_pi(sk)->hdev != hdev)
|
||||
continue;
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
static struct class *bt_class;
|
||||
|
||||
struct dentry *bt_debugfs = NULL;
|
||||
struct dentry *bt_debugfs;
|
||||
EXPORT_SYMBOL_GPL(bt_debugfs);
|
||||
|
||||
static inline char *link_typetostr(int type)
|
||||
@@ -51,8 +51,8 @@ static ssize_t show_link_features(struct device *dev, struct device_attribute *a
|
||||
conn->features[6], conn->features[7]);
|
||||
}
|
||||
|
||||
#define LINK_ATTR(_name,_mode,_show,_store) \
|
||||
struct device_attribute link_attr_##_name = __ATTR(_name,_mode,_show,_store)
|
||||
#define LINK_ATTR(_name, _mode, _show, _store) \
|
||||
struct device_attribute link_attr_##_name = __ATTR(_name, _mode, _show, _store)
|
||||
|
||||
static LINK_ATTR(type, S_IRUGO, show_link_type, NULL);
|
||||
static LINK_ATTR(address, S_IRUGO, show_link_address, NULL);
|
||||
@@ -461,6 +461,56 @@ static const struct file_operations blacklist_fops = {
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static void print_bt_uuid(struct seq_file *f, u8 *uuid)
|
||||
{
|
||||
u32 data0, data4;
|
||||
u16 data1, data2, data3, data5;
|
||||
|
||||
memcpy(&data0, &uuid[0], 4);
|
||||
memcpy(&data1, &uuid[4], 2);
|
||||
memcpy(&data2, &uuid[6], 2);
|
||||
memcpy(&data3, &uuid[8], 2);
|
||||
memcpy(&data4, &uuid[10], 4);
|
||||
memcpy(&data5, &uuid[14], 2);
|
||||
|
||||
seq_printf(f, "%.8x-%.4x-%.4x-%.4x-%.8x%.4x\n",
|
||||
ntohl(data0), ntohs(data1), ntohs(data2),
|
||||
ntohs(data3), ntohl(data4), ntohs(data5));
|
||||
}
|
||||
|
||||
static int uuids_show(struct seq_file *f, void *p)
|
||||
{
|
||||
struct hci_dev *hdev = f->private;
|
||||
struct list_head *l;
|
||||
|
||||
hci_dev_lock_bh(hdev);
|
||||
|
||||
list_for_each(l, &hdev->uuids) {
|
||||
struct bt_uuid *uuid;
|
||||
|
||||
uuid = list_entry(l, struct bt_uuid, list);
|
||||
|
||||
print_bt_uuid(f, uuid->uuid);
|
||||
}
|
||||
|
||||
hci_dev_unlock_bh(hdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uuids_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, uuids_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations uuids_fops = {
|
||||
.open = uuids_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
int hci_register_sysfs(struct hci_dev *hdev)
|
||||
{
|
||||
struct device *dev = &hdev->dev;
|
||||
@@ -493,6 +543,8 @@ int hci_register_sysfs(struct hci_dev *hdev)
|
||||
debugfs_create_file("blacklist", 0444, hdev->debugfs,
|
||||
hdev, &blacklist_fops);
|
||||
|
||||
debugfs_create_file("uuids", 0444, hdev->debugfs, hdev, &uuids_fops);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -157,7 +157,8 @@ static int hidp_queue_event(struct hidp_session *session, struct input_dev *dev,
|
||||
|
||||
session->leds = newleds;
|
||||
|
||||
if (!(skb = alloc_skb(3, GFP_ATOMIC))) {
|
||||
skb = alloc_skb(3, GFP_ATOMIC);
|
||||
if (!skb) {
|
||||
BT_ERR("Can't allocate memory for new frame");
|
||||
return -ENOMEM;
|
||||
}
|
||||
@@ -250,7 +251,8 @@ static int __hidp_send_ctrl_message(struct hidp_session *session,
|
||||
|
||||
BT_DBG("session %p data %p size %d", session, data, size);
|
||||
|
||||
if (!(skb = alloc_skb(size + 1, GFP_ATOMIC))) {
|
||||
skb = alloc_skb(size + 1, GFP_ATOMIC);
|
||||
if (!skb) {
|
||||
BT_ERR("Can't allocate memory for new frame");
|
||||
return -ENOMEM;
|
||||
}
|
||||
@@ -283,7 +285,8 @@ static int hidp_queue_report(struct hidp_session *session,
|
||||
|
||||
BT_DBG("session %p hid %p data %p size %d", session, session->hid, data, size);
|
||||
|
||||
if (!(skb = alloc_skb(size + 1, GFP_ATOMIC))) {
|
||||
skb = alloc_skb(size + 1, GFP_ATOMIC);
|
||||
if (!skb) {
|
||||
BT_ERR("Can't allocate memory for new frame");
|
||||
return -ENOMEM;
|
||||
}
|
||||
@@ -1016,8 +1019,6 @@ static int __init hidp_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
l2cap_load();
|
||||
|
||||
BT_INFO("HIDP (Human Interface Emulation) ver %s", VERSION);
|
||||
|
||||
ret = hid_register_driver(&hidp_driver);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+1174
-90
File diff suppressed because it is too large
Load Diff
@@ -2154,8 +2154,6 @@ static int __init rfcomm_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
l2cap_load();
|
||||
|
||||
hci_register_cb(&rfcomm_cb);
|
||||
|
||||
rfcomm_thread = kthread_run(rfcomm_run, NULL, "krfcommd");
|
||||
|
||||
@@ -727,7 +727,9 @@ static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp)
|
||||
break;
|
||||
}
|
||||
|
||||
tty_unlock();
|
||||
schedule();
|
||||
tty_lock();
|
||||
}
|
||||
set_current_state(TASK_RUNNING);
|
||||
remove_wait_queue(&dev->wait, &wait);
|
||||
|
||||
+3
-14
@@ -50,8 +50,6 @@
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
#include <net/bluetooth/sco.h>
|
||||
|
||||
#define VERSION "0.6"
|
||||
|
||||
static int disable_esco;
|
||||
|
||||
static const struct proto_ops sco_sock_ops;
|
||||
@@ -703,6 +701,7 @@ static int sco_sock_getsockopt_old(struct socket *sock, int optname, char __user
|
||||
break;
|
||||
}
|
||||
|
||||
memset(&cinfo, 0, sizeof(cinfo));
|
||||
cinfo.hci_handle = sco_pi(sk)->conn->hcon->handle;
|
||||
memcpy(cinfo.dev_class, sco_pi(sk)->conn->hcon->dev_class, 3);
|
||||
|
||||
@@ -1023,7 +1022,7 @@ static struct hci_proto sco_hci_proto = {
|
||||
.recv_scodata = sco_recv_scodata
|
||||
};
|
||||
|
||||
static int __init sco_init(void)
|
||||
int __init sco_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
@@ -1051,7 +1050,6 @@ static int __init sco_init(void)
|
||||
BT_ERR("Failed to create SCO debug file");
|
||||
}
|
||||
|
||||
BT_INFO("SCO (Voice Link) ver %s", VERSION);
|
||||
BT_INFO("SCO socket layer initialized");
|
||||
|
||||
return 0;
|
||||
@@ -1061,7 +1059,7 @@ error:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit sco_exit(void)
|
||||
void __exit sco_exit(void)
|
||||
{
|
||||
debugfs_remove(sco_debugfs);
|
||||
|
||||
@@ -1074,14 +1072,5 @@ static void __exit sco_exit(void)
|
||||
proto_unregister(&sco_proto);
|
||||
}
|
||||
|
||||
module_init(sco_init);
|
||||
module_exit(sco_exit);
|
||||
|
||||
module_param(disable_esco, bool, 0644);
|
||||
MODULE_PARM_DESC(disable_esco, "Disable eSCO connection creation");
|
||||
|
||||
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
|
||||
MODULE_DESCRIPTION("Bluetooth SCO ver " VERSION);
|
||||
MODULE_VERSION(VERSION);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("bt-proto-2");
|
||||
|
||||
@@ -17,7 +17,7 @@ comment "CFG80211 needs to be enabled for MAC80211"
|
||||
if MAC80211 != n
|
||||
|
||||
config MAC80211_HAS_RC
|
||||
def_bool n
|
||||
bool
|
||||
|
||||
config MAC80211_RC_PID
|
||||
bool "PID controller based rate control algorithm" if EXPERT
|
||||
|
||||
@@ -1863,6 +1863,7 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
|
||||
|
||||
wk->type = IEEE80211_WORK_OFFCHANNEL_TX;
|
||||
wk->chan = chan;
|
||||
wk->chan_type = channel_type;
|
||||
wk->sdata = sdata;
|
||||
wk->done = ieee80211_offchan_tx_done;
|
||||
wk->offchan_tx.frame = skb;
|
||||
|
||||
@@ -60,6 +60,10 @@ static const struct file_operations name## _ops = { \
|
||||
debugfs_create_file(#name, mode, phyd, local, &name## _ops);
|
||||
|
||||
|
||||
DEBUGFS_READONLY_FILE(user_power, "%d",
|
||||
local->user_power_level);
|
||||
DEBUGFS_READONLY_FILE(power, "%d",
|
||||
local->hw.conf.power_level);
|
||||
DEBUGFS_READONLY_FILE(frequency, "%d",
|
||||
local->hw.conf.channel->center_freq);
|
||||
DEBUGFS_READONLY_FILE(total_ps_buffered, "%d",
|
||||
@@ -391,6 +395,8 @@ void debugfs_hw_add(struct ieee80211_local *local)
|
||||
DEBUGFS_ADD(uapsd_queues);
|
||||
DEBUGFS_ADD(uapsd_max_sp_len);
|
||||
DEBUGFS_ADD(channel_type);
|
||||
DEBUGFS_ADD(user_power);
|
||||
DEBUGFS_ADD(power);
|
||||
|
||||
statsd = debugfs_create_dir("statistics", phyd);
|
||||
|
||||
|
||||
+6
-5
@@ -664,12 +664,13 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
|
||||
}
|
||||
|
||||
static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_mgmt *mgmt,
|
||||
size_t len)
|
||||
struct sk_buff *req)
|
||||
{
|
||||
struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(req);
|
||||
struct ieee80211_mgmt *mgmt = (void *)req->data;
|
||||
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
int tx_last_beacon;
|
||||
int tx_last_beacon, len = req->len;
|
||||
struct sk_buff *skb;
|
||||
struct ieee80211_mgmt *resp;
|
||||
u8 *pos, *end;
|
||||
@@ -689,7 +690,7 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
|
||||
mgmt->bssid, tx_last_beacon);
|
||||
#endif /* CONFIG_MAC80211_IBSS_DEBUG */
|
||||
|
||||
if (!tx_last_beacon)
|
||||
if (!tx_last_beacon && !(rx_status->rx_flags & IEEE80211_RX_RA_MATCH))
|
||||
return;
|
||||
|
||||
if (memcmp(mgmt->bssid, ifibss->bssid, ETH_ALEN) != 0 &&
|
||||
@@ -786,7 +787,7 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
|
||||
|
||||
switch (fc & IEEE80211_FCTL_STYPE) {
|
||||
case IEEE80211_STYPE_PROBE_REQ:
|
||||
ieee80211_rx_mgmt_probe_req(sdata, mgmt, skb->len);
|
||||
ieee80211_rx_mgmt_probe_req(sdata, skb);
|
||||
break;
|
||||
case IEEE80211_STYPE_PROBE_RESP:
|
||||
ieee80211_rx_mgmt_probe_resp(sdata, mgmt, skb->len,
|
||||
|
||||
@@ -1066,8 +1066,6 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
|
||||
void ieee80211_configure_filter(struct ieee80211_local *local);
|
||||
u32 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata);
|
||||
|
||||
extern bool ieee80211_disable_40mhz_24ghz;
|
||||
|
||||
/* STA code */
|
||||
void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata);
|
||||
int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
|
||||
|
||||
+29
-4
@@ -34,7 +34,7 @@
|
||||
#include "debugfs.h"
|
||||
|
||||
|
||||
bool ieee80211_disable_40mhz_24ghz;
|
||||
static bool ieee80211_disable_40mhz_24ghz;
|
||||
module_param(ieee80211_disable_40mhz_24ghz, bool, 0644);
|
||||
MODULE_PARM_DESC(ieee80211_disable_40mhz_24ghz,
|
||||
"Disable 40MHz support in the 2.4GHz band");
|
||||
@@ -112,7 +112,13 @@ bool ieee80211_cfg_on_oper_channel(struct ieee80211_local *local)
|
||||
/* This logic needs to match logic in ieee80211_hw_config */
|
||||
if (local->scan_channel) {
|
||||
chan = local->scan_channel;
|
||||
channel_type = NL80211_CHAN_NO_HT;
|
||||
/* If scanning on oper channel, use whatever channel-type
|
||||
* is currently in use.
|
||||
*/
|
||||
if (chan == local->oper_channel)
|
||||
channel_type = local->_oper_channel_type;
|
||||
else
|
||||
channel_type = NL80211_CHAN_NO_HT;
|
||||
} else if (local->tmp_channel) {
|
||||
chan = scan_chan = local->tmp_channel;
|
||||
channel_type = local->tmp_channel_type;
|
||||
@@ -151,7 +157,13 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
|
||||
offchannel_flag = local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL;
|
||||
if (scan_chan) {
|
||||
chan = scan_chan;
|
||||
channel_type = NL80211_CHAN_NO_HT;
|
||||
/* If scanning on oper channel, use whatever channel-type
|
||||
* is currently in use.
|
||||
*/
|
||||
if (chan == local->oper_channel)
|
||||
channel_type = local->_oper_channel_type;
|
||||
else
|
||||
channel_type = NL80211_CHAN_NO_HT;
|
||||
} else if (local->tmp_channel) {
|
||||
chan = scan_chan = local->tmp_channel;
|
||||
channel_type = local->tmp_channel_type;
|
||||
@@ -187,7 +199,8 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
|
||||
changed |= IEEE80211_CONF_CHANGE_SMPS;
|
||||
}
|
||||
|
||||
if (scan_chan)
|
||||
if ((local->scanning & SCAN_SW_SCANNING) ||
|
||||
(local->scanning & SCAN_HW_SCANNING))
|
||||
power = chan->max_power;
|
||||
else
|
||||
power = local->power_constr_level ?
|
||||
@@ -710,6 +723,18 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
|
||||
}
|
||||
channels += sband->n_channels;
|
||||
|
||||
/*
|
||||
* Since ieee80211_disable_40mhz_24ghz is global, we can
|
||||
* modify the sband's ht data even if the driver uses a
|
||||
* global structure for that.
|
||||
*/
|
||||
if (ieee80211_disable_40mhz_24ghz &&
|
||||
band == IEEE80211_BAND_2GHZ &&
|
||||
sband->ht_cap.ht_supported) {
|
||||
sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
|
||||
sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SGI_40;
|
||||
}
|
||||
|
||||
if (max_bitrates < sband->n_bitrates)
|
||||
max_bitrates = sband->n_bitrates;
|
||||
supp_ht = supp_ht || sband->ht_cap.ht_supported;
|
||||
|
||||
@@ -1071,6 +1071,12 @@ void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
|
||||
if (is_multicast_ether_addr(hdr->addr1))
|
||||
return;
|
||||
|
||||
/*
|
||||
* In case we receive frames after disassociation.
|
||||
*/
|
||||
if (!sdata->u.mgd.associated)
|
||||
return;
|
||||
|
||||
ieee80211_sta_reset_conn_monitor(sdata);
|
||||
}
|
||||
|
||||
@@ -2294,6 +2300,7 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
|
||||
else
|
||||
wk->type = IEEE80211_WORK_DIRECT_PROBE;
|
||||
wk->chan = req->bss->channel;
|
||||
wk->chan_type = NL80211_CHAN_NO_HT;
|
||||
wk->sdata = sdata;
|
||||
wk->done = ieee80211_probe_auth_done;
|
||||
|
||||
@@ -2443,6 +2450,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
|
||||
memcpy(wk->assoc.prev_bssid, req->prev_bssid, ETH_ALEN);
|
||||
|
||||
wk->chan = req->bss->channel;
|
||||
wk->chan_type = NL80211_CHAN_NO_HT;
|
||||
wk->sdata = sdata;
|
||||
wk->done = ieee80211_assoc_done;
|
||||
if (!bss->dtim_period &&
|
||||
|
||||
+1
-11
@@ -832,18 +832,8 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
|
||||
ieee80211_is_pspoll(hdr->frame_control)) &&
|
||||
rx->sdata->vif.type != NL80211_IFTYPE_ADHOC &&
|
||||
rx->sdata->vif.type != NL80211_IFTYPE_WDS &&
|
||||
(!rx->sta || !test_sta_flags(rx->sta, WLAN_STA_ASSOC)))) {
|
||||
if ((!ieee80211_has_fromds(hdr->frame_control) &&
|
||||
!ieee80211_has_tods(hdr->frame_control) &&
|
||||
ieee80211_is_data(hdr->frame_control)) ||
|
||||
!(status->rx_flags & IEEE80211_RX_RA_MATCH)) {
|
||||
/* Drop IBSS frames and frames for other hosts
|
||||
* silently. */
|
||||
return RX_DROP_MONITOR;
|
||||
}
|
||||
|
||||
(!rx->sta || !test_sta_flags(rx->sta, WLAN_STA_ASSOC))))
|
||||
return RX_DROP_MONITOR;
|
||||
}
|
||||
|
||||
return RX_CONTINUE;
|
||||
}
|
||||
|
||||
+10
-5
@@ -307,11 +307,15 @@ static void __ieee80211_scan_completed_finish(struct ieee80211_hw *hw,
|
||||
mutex_lock(&local->mtx);
|
||||
on_oper_chan = ieee80211_cfg_on_oper_channel(local);
|
||||
|
||||
WARN_ON(local->scanning & (SCAN_SW_SCANNING | SCAN_HW_SCANNING));
|
||||
|
||||
if (was_hw_scan || !on_oper_chan) {
|
||||
if (WARN_ON(local->scan_channel))
|
||||
local->scan_channel = NULL;
|
||||
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
|
||||
}
|
||||
} else
|
||||
/* Set power back to normal operating levels. */
|
||||
ieee80211_hw_config(local, 0);
|
||||
|
||||
if (!was_hw_scan) {
|
||||
bool on_oper_chan2;
|
||||
@@ -377,6 +381,9 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local)
|
||||
|
||||
ieee80211_configure_filter(local);
|
||||
|
||||
/* We need to set power level at maximum rate for scanning. */
|
||||
ieee80211_hw_config(local, 0);
|
||||
|
||||
ieee80211_queue_delayed_work(&local->hw,
|
||||
&local->scan_work,
|
||||
IEEE80211_CHANNEL_TIME);
|
||||
@@ -517,8 +524,7 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local,
|
||||
|
||||
if (ieee80211_cfg_on_oper_channel(local)) {
|
||||
/* We're currently on operating channel. */
|
||||
if ((next_chan == local->oper_channel) &&
|
||||
(local->_oper_channel_type == NL80211_CHAN_NO_HT))
|
||||
if (next_chan == local->oper_channel)
|
||||
/* We don't need to move off of operating channel. */
|
||||
local->next_scan_state = SCAN_SET_CHANNEL;
|
||||
else
|
||||
@@ -620,8 +626,7 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local,
|
||||
local->scan_channel = chan;
|
||||
|
||||
/* Only call hw-config if we really need to change channels. */
|
||||
if ((chan != local->hw.conf.channel) ||
|
||||
(local->hw.conf.channel_type != NL80211_CHAN_NO_HT))
|
||||
if (chan != local->hw.conf.channel)
|
||||
if (ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL))
|
||||
skip = 1;
|
||||
|
||||
|
||||
@@ -236,6 +236,7 @@ ieee80211_tx_h_dynamic_ps(struct ieee80211_tx_data *tx)
|
||||
if (local->hw.conf.flags & IEEE80211_CONF_PS) {
|
||||
ieee80211_stop_queues_by_reason(&local->hw,
|
||||
IEEE80211_QUEUE_STOP_REASON_PS);
|
||||
ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED;
|
||||
ieee80211_queue_work(&local->hw,
|
||||
&local->dynamic_ps_disable_work);
|
||||
}
|
||||
|
||||
@@ -986,12 +986,6 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
|
||||
u16 cap = sband->ht_cap.cap;
|
||||
__le16 tmp;
|
||||
|
||||
if (ieee80211_disable_40mhz_24ghz &&
|
||||
sband->band == IEEE80211_BAND_2GHZ) {
|
||||
cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
|
||||
cap &= ~IEEE80211_HT_CAP_SGI_40;
|
||||
}
|
||||
|
||||
*pos++ = WLAN_EID_HT_CAPABILITY;
|
||||
*pos++ = sizeof(struct ieee80211_ht_cap);
|
||||
memset(pos, 0, sizeof(struct ieee80211_ht_cap));
|
||||
|
||||
+50
-9
@@ -126,12 +126,6 @@ static void ieee80211_add_ht_ie(struct sk_buff *skb, const u8 *ht_info_ie,
|
||||
|
||||
/* determine capability flags */
|
||||
|
||||
if (ieee80211_disable_40mhz_24ghz &&
|
||||
sband->band == IEEE80211_BAND_2GHZ) {
|
||||
cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
|
||||
cap &= ~IEEE80211_HT_CAP_SGI_40;
|
||||
}
|
||||
|
||||
switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
|
||||
case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
|
||||
if (flags & IEEE80211_CHAN_NO_HT40PLUS) {
|
||||
@@ -874,6 +868,44 @@ static void ieee80211_work_rx_queued_mgmt(struct ieee80211_local *local,
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
static bool ieee80211_work_ct_coexists(enum nl80211_channel_type wk_ct,
|
||||
enum nl80211_channel_type oper_ct)
|
||||
{
|
||||
switch (wk_ct) {
|
||||
case NL80211_CHAN_NO_HT:
|
||||
return true;
|
||||
case NL80211_CHAN_HT20:
|
||||
if (oper_ct != NL80211_CHAN_NO_HT)
|
||||
return true;
|
||||
return false;
|
||||
case NL80211_CHAN_HT40MINUS:
|
||||
case NL80211_CHAN_HT40PLUS:
|
||||
return (wk_ct == oper_ct);
|
||||
}
|
||||
WARN_ON(1); /* shouldn't get here */
|
||||
return false;
|
||||
}
|
||||
|
||||
static enum nl80211_channel_type
|
||||
ieee80211_calc_ct(enum nl80211_channel_type wk_ct,
|
||||
enum nl80211_channel_type oper_ct)
|
||||
{
|
||||
switch (wk_ct) {
|
||||
case NL80211_CHAN_NO_HT:
|
||||
return oper_ct;
|
||||
case NL80211_CHAN_HT20:
|
||||
if (oper_ct != NL80211_CHAN_NO_HT)
|
||||
return oper_ct;
|
||||
return wk_ct;
|
||||
case NL80211_CHAN_HT40MINUS:
|
||||
case NL80211_CHAN_HT40PLUS:
|
||||
return wk_ct;
|
||||
}
|
||||
WARN_ON(1); /* shouldn't get here */
|
||||
return wk_ct;
|
||||
}
|
||||
|
||||
|
||||
static void ieee80211_work_timer(unsigned long data)
|
||||
{
|
||||
struct ieee80211_local *local = (void *) data;
|
||||
@@ -927,14 +959,22 @@ static void ieee80211_work_work(struct work_struct *work)
|
||||
bool on_oper_chan;
|
||||
bool tmp_chan_changed = false;
|
||||
bool on_oper_chan2;
|
||||
enum nl80211_channel_type wk_ct;
|
||||
on_oper_chan = ieee80211_cfg_on_oper_channel(local);
|
||||
|
||||
/* Work with existing channel type if possible. */
|
||||
wk_ct = wk->chan_type;
|
||||
if (wk->chan == local->hw.conf.channel)
|
||||
wk_ct = ieee80211_calc_ct(wk->chan_type,
|
||||
local->hw.conf.channel_type);
|
||||
|
||||
if (local->tmp_channel)
|
||||
if ((local->tmp_channel != wk->chan) ||
|
||||
(local->tmp_channel_type != wk->chan_type))
|
||||
(local->tmp_channel_type != wk_ct))
|
||||
tmp_chan_changed = true;
|
||||
|
||||
local->tmp_channel = wk->chan;
|
||||
local->tmp_channel_type = wk->chan_type;
|
||||
local->tmp_channel_type = wk_ct;
|
||||
/*
|
||||
* Leave the station vifs in awake mode if they
|
||||
* happen to be on the same channel as
|
||||
@@ -1031,7 +1071,8 @@ static void ieee80211_work_work(struct work_struct *work)
|
||||
continue;
|
||||
if (wk->chan != local->tmp_channel)
|
||||
continue;
|
||||
if (wk->chan_type != local->tmp_channel_type)
|
||||
if (ieee80211_work_ct_coexists(wk->chan_type,
|
||||
local->tmp_channel_type))
|
||||
continue;
|
||||
remain_off_channel = true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user