linux/net/bluetooth/hci_conn.c
Dave Young 2ae9a6be5f Bluetooth: Move hci_conn_del_sysfs() back to avoid device destruct too early
The following commit introduce a regression:

	commit 7d0db0a373
	Author: Marcel Holtmann <marcel@holtmann.org>
	Date:   Mon Jul 14 20:13:51 2008 +0200

		[Bluetooth] Use a more unique bus name for connections

I get panic as following (by netconsole):

[ 2709.344034] usb 5-1: new full speed USB device using uhci_hcd and address 4
[ 2709.505776] usb 5-1: configuration #1 chosen from 1 choice
[ 2709.569207] Bluetooth: Generic Bluetooth USB driver ver 0.4
[ 2709.570169] usbcore: registered new interface driver btusb
[ 2845.742781] BUG: unable to handle kernel paging request at 6b6b6c2f
[ 2845.742958] IP: [<c015515c>] __lock_acquire+0x6c/0xa80
[ 2845.743087] *pde = 00000000
[ 2845.743206] Oops: 0002 [#1] SMP
[ 2845.743377] last sysfs file: /sys/class/bluetooth/hci0/hci0:6/type
[ 2845.743742] Modules linked in: btusb netconsole snd_seq_dummy snd_seq_oss snd_seq_midi_event snd_seq snd_seq_device snd_pcm_oss snd_mixer_oss rfcomm l2cap bluetooth vfat fuse snd_hda_codec_idt snd_hda_intel snd_hda_codec snd_hwdep snd_pcm pl2303 snd_timer psmouse usbserial snd 3c59x e100 serio_raw soundcore i2c_i801 intel_agp mii agpgart snd_page_alloc rtc_cmos rtc_core thermal processor rtc_lib button thermal_sys sg evdev
[ 2845.743742]
[ 2845.743742] Pid: 0, comm: swapper Not tainted (2.6.29-rc5-smp #54) Dell DM051
[ 2845.743742] EIP: 0060:[<c015515c>] EFLAGS: 00010002 CPU: 0
[ 2845.743742] EIP is at __lock_acquire+0x6c/0xa80
[ 2845.743742] EAX: 00000046 EBX: 00000046 ECX: 6b6b6b6b EDX: 00000002
[ 2845.743742] ESI: 6b6b6b6b EDI: 00000000 EBP: c064fd14 ESP: c064fcc8
[ 2845.743742]  DS: 007b ES: 007b FS: 00d8 GS: 0000 SS: 0068
[ 2845.743742] Process swapper (pid: 0, ti=c064e000 task=c05d1400 task.ti=c064e000)
[ 2845.743742] Stack:
[ 2845.743742]  c05d1400 00000002 c05d1400 00000001 00000002 00000000 f65388dc c05d1400
[ 2845.743742]  6b6b6b6b 00000292 c064fd0c c0153732 00000000 00000000 00000001 f700fa50
[ 2845.743742]  00000046 00000000 00000000 c064fd40 c0155be6 00000000 00000002 00000001
[ 2845.743742] Call Trace:
[ 2845.743742]  [<c0153732>] ? trace_hardirqs_on_caller+0x72/0x1c0
[ 2845.743742]  [<c0155be6>] ? lock_acquire+0x76/0xa0
[ 2845.743742]  [<c03e1aad>] ? skb_dequeue+0x1d/0x70
[ 2845.743742]  [<c046c885>] ? _spin_lock_irqsave+0x45/0x80
[ 2845.743742]  [<c03e1aad>] ? skb_dequeue+0x1d/0x70
[ 2845.743742]  [<c03e1aad>] ? skb_dequeue+0x1d/0x70
[ 2845.743742]  [<c03e1f94>] ? skb_queue_purge+0x14/0x20
[ 2845.743742]  [<f8171f5a>] ? hci_conn_del+0x10a/0x1c0 [bluetooth]
[ 2845.743742]  [<f81399c9>] ? l2cap_disconn_ind+0x59/0xb0 [l2cap]
[ 2845.743742]  [<f81795ce>] ? hci_conn_del_sysfs+0x8e/0xd0 [bluetooth]
[ 2845.743742]  [<f8175758>] ? hci_event_packet+0x5f8/0x31c0 [bluetooth]
[ 2845.743742]  [<c03dfe19>] ? sock_def_readable+0x59/0x80
[ 2845.743742]  [<c046c14d>] ? _read_unlock+0x1d/0x20
[ 2845.743742]  [<f8178aa9>] ? hci_send_to_sock+0xe9/0x1d0 [bluetooth]
[ 2845.743742]  [<c015388b>] ? trace_hardirqs_on+0xb/0x10
[ 2845.743742]  [<f816fa6a>] ? hci_rx_task+0x2ba/0x490 [bluetooth]
[ 2845.743742]  [<c0133661>] ? tasklet_action+0x31/0xc0
[ 2845.743742]  [<c013367c>] ? tasklet_action+0x4c/0xc0
[ 2845.743742]  [<c0132eb7>] ? __do_softirq+0xa7/0x170
[ 2845.743742]  [<c0116dec>] ? ack_apic_level+0x5c/0x1c0
[ 2845.743742]  [<c0132fd7>] ? do_softirq+0x57/0x60
[ 2845.743742]  [<c01333dc>] ? irq_exit+0x7c/0x90
[ 2845.743742]  [<c01055bb>] ? do_IRQ+0x4b/0x90
[ 2845.743742]  [<c01333d5>] ? irq_exit+0x75/0x90
[ 2845.743742]  [<c010392c>] ? common_interrupt+0x2c/0x34
[ 2845.743742]  [<c010a14f>] ? mwait_idle+0x4f/0x70
[ 2845.743742]  [<c0101c05>] ? cpu_idle+0x65/0xb0
[ 2845.743742]  [<c045731e>] ? rest_init+0x4e/0x60
[ 2845.743742] Code: 0f 84 69 02 00 00 83 ff 07 0f 87 1e 06 00 00 85 ff 0f 85 08 05 00 00 8b 4d cc 8b 49 04 85 c9 89 4d d4 0f 84 f7 04 00 00 8b 75 d4 <f0> ff 86 c4 00 00 00 89 f0 e8 56 a9 ff ff 85 c0 0f 85 6e 03 00
[ 2845.743742] EIP: [<c015515c>] __lock_acquire+0x6c/0xa80 SS:ESP 0068:c064fcc8
[ 2845.743742] ---[ end trace 4c985b38f022279f ]---
[ 2845.743742] Kernel panic - not syncing: Fatal exception in interrupt
[ 2845.743742] ------------[ cut here ]------------
[ 2845.743742] WARNING: at kernel/smp.c:329 smp_call_function_many+0x151/0x200()
[ 2845.743742] Hardware name: Dell DM051
[ 2845.743742] Modules linked in: btusb netconsole snd_seq_dummy snd_seq_oss snd_seq_midi_event snd_seq snd_seq_device snd_pcm_oss snd_mixer_oss rfcomm l2cap bluetooth vfat fuse snd_hda_codec_idt snd_hda_intel snd_hda_codec snd_hwdep snd_pcm pl2303 snd_timer psmouse usbserial snd 3c59x e100 serio_raw soundcore i2c_i801 intel_agp mii agpgart snd_page_alloc rtc_cmos rtc_core thermal processor rtc_lib button thermal_sys sg evdev
[ 2845.743742] Pid: 0, comm: swapper Tainted: G      D    2.6.29-rc5-smp #54
[ 2845.743742] Call Trace:
[ 2845.743742]  [<c012e076>] warn_slowpath+0x86/0xa0
[ 2845.743742]  [<c015041b>] ? trace_hardirqs_off+0xb/0x10
[ 2845.743742]  [<c0146384>] ? up+0x14/0x40
[ 2845.743742]  [<c012e661>] ? release_console_sem+0x31/0x1e0
[ 2845.743742]  [<c046c8ab>] ? _spin_lock_irqsave+0x6b/0x80
[ 2845.743742]  [<c015041b>] ? trace_hardirqs_off+0xb/0x10
[ 2845.743742]  [<c046c900>] ? _read_lock_irqsave+0x40/0x80
[ 2845.743742]  [<c012e7f2>] ? release_console_sem+0x1c2/0x1e0
[ 2845.743742]  [<c0146384>] ? up+0x14/0x40
[ 2845.743742]  [<c015041b>] ? trace_hardirqs_off+0xb/0x10
[ 2845.743742]  [<c046a3d7>] ? __mutex_unlock_slowpath+0x97/0x160
[ 2845.743742]  [<c046a563>] ? mutex_trylock+0xb3/0x180
[ 2845.743742]  [<c046a4a8>] ? mutex_unlock+0x8/0x10
[ 2845.743742]  [<c015b991>] smp_call_function_many+0x151/0x200
[ 2845.743742]  [<c010a1a0>] ? stop_this_cpu+0x0/0x40
[ 2845.743742]  [<c015ba61>] smp_call_function+0x21/0x30
[ 2845.743742]  [<c01137ae>] native_smp_send_stop+0x1e/0x50
[ 2845.743742]  [<c012e0f5>] panic+0x55/0x110
[ 2845.743742]  [<c01065a8>] oops_end+0xb8/0xc0
[ 2845.743742]  [<c010668f>] die+0x4f/0x70
[ 2845.743742]  [<c011a8c9>] do_page_fault+0x269/0x610
[ 2845.743742]  [<c011a660>] ? do_page_fault+0x0/0x610
[ 2845.743742]  [<c046cbaf>] error_code+0x77/0x7c
[ 2845.743742]  [<c015515c>] ? __lock_acquire+0x6c/0xa80
[ 2845.743742]  [<c0153732>] ? trace_hardirqs_on_caller+0x72/0x1c0
[ 2845.743742]  [<c0155be6>] lock_acquire+0x76/0xa0
[ 2845.743742]  [<c03e1aad>] ? skb_dequeue+0x1d/0x70
[ 2845.743742]  [<c046c885>] _spin_lock_irqsave+0x45/0x80
[ 2845.743742]  [<c03e1aad>] ? skb_dequeue+0x1d/0x70
[ 2845.743742]  [<c03e1aad>] skb_dequeue+0x1d/0x70
[ 2845.743742]  [<c03e1f94>] skb_queue_purge+0x14/0x20
[ 2845.743742]  [<f8171f5a>] hci_conn_del+0x10a/0x1c0 [bluetooth]
[ 2845.743742]  [<f81399c9>] ? l2cap_disconn_ind+0x59/0xb0 [l2cap]
[ 2845.743742]  [<f81795ce>] ? hci_conn_del_sysfs+0x8e/0xd0 [bluetooth]
[ 2845.743742]  [<f8175758>] hci_event_packet+0x5f8/0x31c0 [bluetooth]
[ 2845.743742]  [<c03dfe19>] ? sock_def_readable+0x59/0x80
[ 2845.743742]  [<c046c14d>] ? _read_unlock+0x1d/0x20
[ 2845.743742]  [<f8178aa9>] ? hci_send_to_sock+0xe9/0x1d0 [bluetooth]
[ 2845.743742]  [<c015388b>] ? trace_hardirqs_on+0xb/0x10
[ 2845.743742]  [<f816fa6a>] hci_rx_task+0x2ba/0x490 [bluetooth]
[ 2845.743742]  [<c0133661>] ? tasklet_action+0x31/0xc0
[ 2845.743742]  [<c013367c>] tasklet_action+0x4c/0xc0
[ 2845.743742]  [<c0132eb7>] __do_softirq+0xa7/0x170
[ 2845.743742]  [<c0116dec>] ? ack_apic_level+0x5c/0x1c0
[ 2845.743742]  [<c0132fd7>] do_softirq+0x57/0x60
[ 2845.743742]  [<c01333dc>] irq_exit+0x7c/0x90
[ 2845.743742]  [<c01055bb>] do_IRQ+0x4b/0x90
[ 2845.743742]  [<c01333d5>] ? irq_exit+0x75/0x90
[ 2845.743742]  [<c010392c>] common_interrupt+0x2c/0x34
[ 2845.743742]  [<c010a14f>] ? mwait_idle+0x4f/0x70
[ 2845.743742]  [<c0101c05>] cpu_idle+0x65/0xb0
[ 2845.743742]  [<c045731e>] rest_init+0x4e/0x60
[ 2845.743742] ---[ end trace 4c985b38f02227a0 ]---
[ 2845.743742] ------------[ cut here ]------------
[ 2845.743742] WARNING: at kernel/smp.c:226 smp_call_function_single+0x8e/0x110()
[ 2845.743742] Hardware name: Dell DM051
[ 2845.743742] Modules linked in: btusb netconsole snd_seq_dummy snd_seq_oss snd_seq_midi_event snd_seq snd_seq_device snd_pcm_oss snd_mixer_oss rfcomm l2cap bluetooth vfat fuse snd_hda_codec_idt snd_hda_intel snd_hda_codec snd_hwdep snd_pcm pl2303 snd_timer psmouse usbserial snd 3c59x e100 serio_raw soundcore i2c_i801 intel_agp mii agpgart snd_page_alloc rtc_cmos rtc_core thermal processor rtc_lib button thermal_sys sg evdev
[ 2845.743742] Pid: 0, comm: swapper Tainted: G      D W  2.6.29-rc5-smp #54
[ 2845.743742] Call Trace:
[ 2845.743742]  [<c012e076>] warn_slowpath+0x86/0xa0
[ 2845.743742]  [<c012e000>] ? warn_slowpath+0x10/0xa0
[ 2845.743742]  [<c015041b>] ? trace_hardirqs_off+0xb/0x10
[ 2845.743742]  [<c0146384>] ? up+0x14/0x40
[ 2845.743742]  [<c012e661>] ? release_console_sem+0x31/0x1e0
[ 2845.743742]  [<c046c8ab>] ? _spin_lock_irqsave+0x6b/0x80
[ 2845.743742]  [<c015041b>] ? trace_hardirqs_off+0xb/0x10
[ 2845.743742]  [<c046c900>] ? _read_lock_irqsave+0x40/0x80
[ 2845.743742]  [<c012e7f2>] ? release_console_sem+0x1c2/0x1e0
[ 2845.743742]  [<c0146384>] ? up+0x14/0x40
[ 2845.743742]  [<c015b7be>] smp_call_function_single+0x8e/0x110
[ 2845.743742]  [<c010a1a0>] ? stop_this_cpu+0x0/0x40
[ 2845.743742]  [<c026d23f>] ? cpumask_next_and+0x1f/0x40
[ 2845.743742]  [<c015b95a>] smp_call_function_many+0x11a/0x200
[ 2845.743742]  [<c010a1a0>] ? stop_this_cpu+0x0/0x40
[ 2845.743742]  [<c015ba61>] smp_call_function+0x21/0x30
[ 2845.743742]  [<c01137ae>] native_smp_send_stop+0x1e/0x50
[ 2845.743742]  [<c012e0f5>] panic+0x55/0x110
[ 2845.743742]  [<c01065a8>] oops_end+0xb8/0xc0
[ 2845.743742]  [<c010668f>] die+0x4f/0x70
[ 2845.743742]  [<c011a8c9>] do_page_fault+0x269/0x610
[ 2845.743742]  [<c011a660>] ? do_page_fault+0x0/0x610
[ 2845.743742]  [<c046cbaf>] error_code+0x77/0x7c
[ 2845.743742]  [<c015515c>] ? __lock_acquire+0x6c/0xa80
[ 2845.743742]  [<c0153732>] ? trace_hardirqs_on_caller+0x72/0x1c0
[ 2845.743742]  [<c0155be6>] lock_acquire+0x76/0xa0
[ 2845.743742]  [<c03e1aad>] ? skb_dequeue+0x1d/0x70
[ 2845.743742]  [<c046c885>] _spin_lock_irqsave+0x45/0x80
[ 2845.743742]  [<c03e1aad>] ? skb_dequeue+0x1d/0x70
[ 2845.743742]  [<c03e1aad>] skb_dequeue+0x1d/0x70
[ 2845.743742]  [<c03e1f94>] skb_queue_purge+0x14/0x20
[ 2845.743742]  [<f8171f5a>] hci_conn_del+0x10a/0x1c0 [bluetooth]
[ 2845.743742]  [<f81399c9>] ? l2cap_disconn_ind+0x59/0xb0 [l2cap]
[ 2845.743742]  [<f81795ce>] ? hci_conn_del_sysfs+0x8e/0xd0 [bluetooth]
[ 2845.743742]  [<f8175758>] hci_event_packet+0x5f8/0x31c0 [bluetooth]
[ 2845.743742]  [<c03dfe19>] ? sock_def_readable+0x59/0x80
[ 2845.743742]  [<c046c14d>] ? _read_unlock+0x1d/0x20
[ 2845.743742]  [<f8178aa9>] ? hci_send_to_sock+0xe9/0x1d0 [bluetooth]
[ 2845.743742]  [<c015388b>] ? trace_hardirqs_on+0xb/0x10
[ 2845.743742]  [<f816fa6a>] hci_rx_task+0x2ba/0x490 [bluetooth]
[ 2845.743742]  [<c0133661>] ? tasklet_action+0x31/0xc0
[ 2845.743742]  [<c013367c>] tasklet_action+0x4c/0xc0
[ 2845.743742]  [<c0132eb7>] __do_softirq+0xa7/0x170
[ 2845.743742]  [<c0116dec>] ? ack_apic_level+0x5c/0x1c0
[ 2845.743742]  [<c0132fd7>] do_softirq+0x57/0x60
[ 2845.743742]  [<c01333dc>] irq_exit+0x7c/0x90
[ 2845.743742]  [<c01055bb>] do_IRQ+0x4b/0x90
[ 2845.743742]  [<c01333d5>] ? irq_exit+0x75/0x90
[ 2845.743742]  [<c010392c>] common_interrupt+0x2c/0x34
[ 2845.743742]  [<c010a14f>] ? mwait_idle+0x4f/0x70
[ 2845.743742]  [<c0101c05>] cpu_idle+0x65/0xb0
[ 2845.743742]  [<c045731e>] rest_init+0x4e/0x60
[ 2845.743742] ---[ end trace 4c985b38f02227a1 ]---
[ 2845.743742] Rebooting in 3 seconds..

My logitec bluetooth mouse trying connect to pc, but
pc side reject the connection again and again. then panic happens.

The reason is due to hci_conn_del_sysfs now called in hci_event_packet,
the del work is done in a workqueue, so it's possible done before
skb_queue_purge called.

I move the hci_conn_del_sysfs after skb_queue_purge just as that before
marcel's commit.

Remove the hci_conn_del_sysfs in hci_conn_hash_flush as well due to
hci_conn_del will deal with the work.

Signed-off-by: Dave Young <hidave.darkstar@gmail.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
2009-02-27 06:14:49 +01:00

687 lines
15 KiB
C

/*
BlueZ - Bluetooth protocol stack for Linux
Copyright (C) 2000-2001 Qualcomm Incorporated
Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation;
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
SOFTWARE IS DISCLAIMED.
*/
/* Bluetooth HCI connection handling. */
#include <linux/module.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/poll.h>
#include <linux/fcntl.h>
#include <linux/init.h>
#include <linux/skbuff.h>
#include <linux/interrupt.h>
#include <linux/notifier.h>
#include <net/sock.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/unaligned.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
void hci_acl_connect(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
struct inquiry_entry *ie;
struct hci_cp_create_conn cp;
BT_DBG("%p", conn);
conn->state = BT_CONNECT;
conn->out = 1;
conn->link_mode = HCI_LM_MASTER;
conn->attempt++;
conn->link_policy = hdev->link_policy;
memset(&cp, 0, sizeof(cp));
bacpy(&cp.bdaddr, &conn->dst);
cp.pscan_rep_mode = 0x02;
if ((ie = hci_inquiry_cache_lookup(hdev, &conn->dst))) {
if (inquiry_entry_age(ie) <= INQUIRY_ENTRY_AGE_MAX) {
cp.pscan_rep_mode = ie->data.pscan_rep_mode;
cp.pscan_mode = ie->data.pscan_mode;
cp.clock_offset = ie->data.clock_offset |
cpu_to_le16(0x8000);
}
memcpy(conn->dev_class, ie->data.dev_class, 3);
conn->ssp_mode = ie->data.ssp_mode;
}
cp.pkt_type = cpu_to_le16(conn->pkt_type);
if (lmp_rswitch_capable(hdev) && !(hdev->link_mode & HCI_LM_MASTER))
cp.role_switch = 0x01;
else
cp.role_switch = 0x00;
hci_send_cmd(hdev, HCI_OP_CREATE_CONN, sizeof(cp), &cp);
}
static void hci_acl_connect_cancel(struct hci_conn *conn)
{
struct hci_cp_create_conn_cancel cp;
BT_DBG("%p", conn);
if (conn->hdev->hci_ver < 2)
return;
bacpy(&cp.bdaddr, &conn->dst);
hci_send_cmd(conn->hdev, HCI_OP_CREATE_CONN_CANCEL, sizeof(cp), &cp);
}
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);
}
void hci_add_sco(struct hci_conn *conn, __u16 handle)
{
struct hci_dev *hdev = conn->hdev;
struct hci_cp_add_sco cp;
BT_DBG("%p", conn);
conn->state = BT_CONNECT;
conn->out = 1;
conn->attempt++;
cp.handle = cpu_to_le16(handle);
cp.pkt_type = cpu_to_le16(conn->pkt_type);
hci_send_cmd(hdev, HCI_OP_ADD_SCO, sizeof(cp), &cp);
}
void hci_setup_sync(struct hci_conn *conn, __u16 handle)
{
struct hci_dev *hdev = conn->hdev;
struct hci_cp_setup_sync_conn cp;
BT_DBG("%p", conn);
conn->state = BT_CONNECT;
conn->out = 1;
conn->attempt++;
cp.handle = cpu_to_le16(handle);
cp.pkt_type = cpu_to_le16(conn->pkt_type);
cp.tx_bandwidth = cpu_to_le32(0x00001f40);
cp.rx_bandwidth = cpu_to_le32(0x00001f40);
cp.max_latency = cpu_to_le16(0xffff);
cp.voice_setting = cpu_to_le16(hdev->voice_setting);
cp.retrans_effort = 0xff;
hci_send_cmd(hdev, HCI_OP_SETUP_SYNC_CONN, sizeof(cp), &cp);
}
static void hci_conn_timeout(unsigned long arg)
{
struct hci_conn *conn = (void *) arg;
struct hci_dev *hdev = conn->hdev;
__u8 reason;
BT_DBG("conn %p state %d", conn, conn->state);
if (atomic_read(&conn->refcnt))
return;
hci_dev_lock(hdev);
switch (conn->state) {
case BT_CONNECT:
case BT_CONNECT2:
if (conn->type == ACL_LINK)
hci_acl_connect_cancel(conn);
else
hci_acl_disconn(conn, 0x13);
break;
case BT_CONFIG:
case BT_CONNECTED:
reason = hci_proto_disconn_ind(conn);
hci_acl_disconn(conn, reason);
break;
default:
conn->state = BT_CLOSED;
break;
}
hci_dev_unlock(hdev);
}
static void hci_conn_idle(unsigned long arg)
{
struct hci_conn *conn = (void *) arg;
BT_DBG("conn %p mode %d", conn, conn->mode);
hci_conn_enter_sniff_mode(conn);
}
struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
{
struct hci_conn *conn;
BT_DBG("%s dst %s", hdev->name, batostr(dst));
conn = kzalloc(sizeof(struct hci_conn), GFP_ATOMIC);
if (!conn)
return NULL;
bacpy(&conn->dst, dst);
conn->hdev = hdev;
conn->type = type;
conn->mode = HCI_CM_ACTIVE;
conn->state = BT_OPEN;
conn->power_save = 1;
switch (type) {
case ACL_LINK:
conn->pkt_type = hdev->pkt_type & ACL_PTYPE_MASK;
break;
case SCO_LINK:
if (lmp_esco_capable(hdev))
conn->pkt_type = (hdev->esco_type & SCO_ESCO_MASK) |
(hdev->esco_type & EDR_ESCO_MASK);
else
conn->pkt_type = hdev->pkt_type & SCO_PTYPE_MASK;
break;
case ESCO_LINK:
conn->pkt_type = hdev->esco_type & ~EDR_ESCO_MASK;
break;
}
skb_queue_head_init(&conn->data_q);
setup_timer(&conn->disc_timer, hci_conn_timeout, (unsigned long)conn);
setup_timer(&conn->idle_timer, hci_conn_idle, (unsigned long)conn);
atomic_set(&conn->refcnt, 0);
hci_dev_hold(hdev);
tasklet_disable(&hdev->tx_task);
hci_conn_hash_add(hdev, conn);
if (hdev->notify)
hdev->notify(hdev, HCI_NOTIFY_CONN_ADD);
tasklet_enable(&hdev->tx_task);
return conn;
}
int hci_conn_del(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
BT_DBG("%s conn %p handle %d", hdev->name, conn, conn->handle);
del_timer(&conn->idle_timer);
del_timer(&conn->disc_timer);
if (conn->type == ACL_LINK) {
struct hci_conn *sco = conn->link;
if (sco)
sco->link = NULL;
/* Unacked frames */
hdev->acl_cnt += conn->sent;
} else {
struct hci_conn *acl = conn->link;
if (acl) {
acl->link = NULL;
hci_conn_put(acl);
}
}
tasklet_disable(&hdev->tx_task);
hci_conn_hash_del(hdev, conn);
if (hdev->notify)
hdev->notify(hdev, HCI_NOTIFY_CONN_DEL);
tasklet_enable(&hdev->tx_task);
skb_queue_purge(&conn->data_q);
hci_conn_del_sysfs(conn);
return 0;
}
struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src)
{
int use_src = bacmp(src, BDADDR_ANY);
struct hci_dev *hdev = NULL;
struct list_head *p;
BT_DBG("%s -> %s", batostr(src), batostr(dst));
read_lock_bh(&hci_dev_list_lock);
list_for_each(p, &hci_dev_list) {
struct hci_dev *d = list_entry(p, struct hci_dev, list);
if (!test_bit(HCI_UP, &d->flags) || test_bit(HCI_RAW, &d->flags))
continue;
/* Simple routing:
* No source address - find interface with bdaddr != dst
* Source address - find interface with bdaddr == src
*/
if (use_src) {
if (!bacmp(&d->bdaddr, src)) {
hdev = d; break;
}
} else {
if (bacmp(&d->bdaddr, dst)) {
hdev = d; break;
}
}
}
if (hdev)
hdev = hci_dev_hold(hdev);
read_unlock_bh(&hci_dev_list_lock);
return hdev;
}
EXPORT_SYMBOL(hci_get_route);
/* Create SCO or ACL 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;
BT_DBG("%s dst %s", hdev->name, batostr(dst));
if (!(acl = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst))) {
if (!(acl = hci_conn_add(hdev, ACL_LINK, dst)))
return NULL;
}
hci_conn_hold(acl);
if (acl->state == BT_OPEN || acl->state == BT_CLOSED) {
acl->sec_level = sec_level;
acl->auth_type = auth_type;
hci_acl_connect(acl);
}
if (type == ACL_LINK)
return acl;
if (!(sco = hci_conn_hash_lookup_ba(hdev, type, dst))) {
if (!(sco = hci_conn_add(hdev, type, dst))) {
hci_conn_put(acl);
return NULL;
}
}
acl->link = sco;
sco->link = acl;
hci_conn_hold(sco);
if (acl->state == BT_CONNECTED &&
(sco->state == BT_OPEN || sco->state == BT_CLOSED)) {
if (lmp_esco_capable(hdev))
hci_setup_sync(sco, acl->handle);
else
hci_add_sco(sco, acl->handle);
}
return sco;
}
EXPORT_SYMBOL(hci_connect);
/* Check link security requirement */
int hci_conn_check_link_mode(struct hci_conn *conn)
{
BT_DBG("conn %p", conn);
if (conn->ssp_mode > 0 && conn->hdev->ssp_mode > 0 &&
!(conn->link_mode & HCI_LM_ENCRYPT))
return 0;
return 1;
}
EXPORT_SYMBOL(hci_conn_check_link_mode);
/* Authenticate remote device */
static int hci_conn_auth(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
{
BT_DBG("conn %p", conn);
if (sec_level > conn->sec_level)
conn->sec_level = sec_level;
else if (conn->link_mode & HCI_LM_AUTH)
return 1;
conn->auth_type = auth_type;
if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->pend)) {
struct hci_cp_auth_requested cp;
cp.handle = cpu_to_le16(conn->handle);
hci_send_cmd(conn->hdev, HCI_OP_AUTH_REQUESTED,
sizeof(cp), &cp);
}
return 0;
}
/* Enable security */
int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
{
BT_DBG("conn %p", conn);
if (sec_level == BT_SECURITY_SDP)
return 1;
if (sec_level == BT_SECURITY_LOW) {
if (conn->ssp_mode > 0 && conn->hdev->ssp_mode > 0)
return hci_conn_auth(conn, sec_level, auth_type);
else
return 1;
}
if (conn->link_mode & HCI_LM_ENCRYPT)
return hci_conn_auth(conn, sec_level, auth_type);
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);
/* Change link key */
int hci_conn_change_link_key(struct hci_conn *conn)
{
BT_DBG("conn %p", conn);
if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->pend)) {
struct hci_cp_change_conn_link_key cp;
cp.handle = cpu_to_le16(conn->handle);
hci_send_cmd(conn->hdev, HCI_OP_CHANGE_CONN_LINK_KEY,
sizeof(cp), &cp);
}
return 0;
}
EXPORT_SYMBOL(hci_conn_change_link_key);
/* Switch role */
int hci_conn_switch_role(struct hci_conn *conn, __u8 role)
{
BT_DBG("conn %p", conn);
if (!role && conn->link_mode & HCI_LM_MASTER)
return 1;
if (!test_and_set_bit(HCI_CONN_RSWITCH_PEND, &conn->pend)) {
struct hci_cp_switch_role cp;
bacpy(&cp.bdaddr, &conn->dst);
cp.role = role;
hci_send_cmd(conn->hdev, HCI_OP_SWITCH_ROLE, sizeof(cp), &cp);
}
return 0;
}
EXPORT_SYMBOL(hci_conn_switch_role);
/* Enter active mode */
void hci_conn_enter_active_mode(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
BT_DBG("conn %p mode %d", conn, conn->mode);
if (test_bit(HCI_RAW, &hdev->flags))
return;
if (conn->mode != HCI_CM_SNIFF || !conn->power_save)
goto timer;
if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend)) {
struct hci_cp_exit_sniff_mode cp;
cp.handle = cpu_to_le16(conn->handle);
hci_send_cmd(hdev, HCI_OP_EXIT_SNIFF_MODE, sizeof(cp), &cp);
}
timer:
if (hdev->idle_timeout > 0)
mod_timer(&conn->idle_timer,
jiffies + msecs_to_jiffies(hdev->idle_timeout));
}
/* Enter sniff mode */
void hci_conn_enter_sniff_mode(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
BT_DBG("conn %p mode %d", conn, conn->mode);
if (test_bit(HCI_RAW, &hdev->flags))
return;
if (!lmp_sniff_capable(hdev) || !lmp_sniff_capable(conn))
return;
if (conn->mode != HCI_CM_ACTIVE || !(conn->link_policy & HCI_LP_SNIFF))
return;
if (lmp_sniffsubr_capable(hdev) && lmp_sniffsubr_capable(conn)) {
struct hci_cp_sniff_subrate cp;
cp.handle = cpu_to_le16(conn->handle);
cp.max_latency = cpu_to_le16(0);
cp.min_remote_timeout = cpu_to_le16(0);
cp.min_local_timeout = cpu_to_le16(0);
hci_send_cmd(hdev, HCI_OP_SNIFF_SUBRATE, sizeof(cp), &cp);
}
if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend)) {
struct hci_cp_sniff_mode cp;
cp.handle = cpu_to_le16(conn->handle);
cp.max_interval = cpu_to_le16(hdev->sniff_max_interval);
cp.min_interval = cpu_to_le16(hdev->sniff_min_interval);
cp.attempt = cpu_to_le16(4);
cp.timeout = cpu_to_le16(1);
hci_send_cmd(hdev, HCI_OP_SNIFF_MODE, sizeof(cp), &cp);
}
}
/* Drop all connection on the device */
void hci_conn_hash_flush(struct hci_dev *hdev)
{
struct hci_conn_hash *h = &hdev->conn_hash;
struct list_head *p;
BT_DBG("hdev %s", hdev->name);
p = h->list.next;
while (p != &h->list) {
struct hci_conn *c;
c = list_entry(p, struct hci_conn, list);
p = p->next;
c->state = BT_CLOSED;
hci_proto_disconn_cfm(c, 0x16);
hci_conn_del(c);
}
}
/* Check pending connect attempts */
void hci_conn_check_pending(struct hci_dev *hdev)
{
struct hci_conn *conn;
BT_DBG("hdev %s", hdev->name);
hci_dev_lock(hdev);
conn = hci_conn_hash_lookup_state(hdev, ACL_LINK, BT_CONNECT2);
if (conn)
hci_acl_connect(conn);
hci_dev_unlock(hdev);
}
int hci_get_conn_list(void __user *arg)
{
struct hci_conn_list_req req, *cl;
struct hci_conn_info *ci;
struct hci_dev *hdev;
struct list_head *p;
int n = 0, size, err;
if (copy_from_user(&req, arg, sizeof(req)))
return -EFAULT;
if (!req.conn_num || req.conn_num > (PAGE_SIZE * 2) / sizeof(*ci))
return -EINVAL;
size = sizeof(req) + req.conn_num * sizeof(*ci);
if (!(cl = kmalloc(size, GFP_KERNEL)))
return -ENOMEM;
if (!(hdev = hci_dev_get(req.dev_id))) {
kfree(cl);
return -ENODEV;
}
ci = cl->conn_info;
hci_dev_lock_bh(hdev);
list_for_each(p, &hdev->conn_hash.list) {
register struct hci_conn *c;
c = list_entry(p, struct hci_conn, list);
bacpy(&(ci + n)->bdaddr, &c->dst);
(ci + n)->handle = c->handle;
(ci + n)->type = c->type;
(ci + n)->out = c->out;
(ci + n)->state = c->state;
(ci + n)->link_mode = c->link_mode;
if (++n >= req.conn_num)
break;
}
hci_dev_unlock_bh(hdev);
cl->dev_id = hdev->id;
cl->conn_num = n;
size = sizeof(req) + n * sizeof(*ci);
hci_dev_put(hdev);
err = copy_to_user(arg, cl, size);
kfree(cl);
return err ? -EFAULT : 0;
}
int hci_get_conn_info(struct hci_dev *hdev, void __user *arg)
{
struct hci_conn_info_req req;
struct hci_conn_info ci;
struct hci_conn *conn;
char __user *ptr = arg + sizeof(req);
if (copy_from_user(&req, arg, sizeof(req)))
return -EFAULT;
hci_dev_lock_bh(hdev);
conn = hci_conn_hash_lookup_ba(hdev, req.type, &req.bdaddr);
if (conn) {
bacpy(&ci.bdaddr, &conn->dst);
ci.handle = conn->handle;
ci.type = conn->type;
ci.out = conn->out;
ci.state = conn->state;
ci.link_mode = conn->link_mode;
}
hci_dev_unlock_bh(hdev);
if (!conn)
return -ENOENT;
return copy_to_user(ptr, &ci, sizeof(ci)) ? -EFAULT : 0;
}
int hci_get_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)
req.type = conn->auth_type;
hci_dev_unlock_bh(hdev);
if (!conn)
return -ENOENT;
return copy_to_user(arg, &req, sizeof(req)) ? -EFAULT : 0;
}