Merge tag 'for-net-next-2026-06-11' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next
Luiz Augusto von Dentz says: ==================== bluetooth-next pull request for net-next: core: - hci_sync: Add support for HCI_LE_Set_Host_Feature [v2] - SMP: Use AES-CMAC library API - sockets: convert to getsockopt_iter - Add SPDX id lines to some source files drivers: - btintel_pcie: Support Product level reset - btintel_pcie: Add support for smart trigger dump - btintel_pcie: Add 50 ms delay before MAC init on BlazarIW - btintel_pcie: Separate coredump work from RX work - btmtk: add event filter to filter specific event - btrtl: fix RTL8761B/BU broken LE extended scan - btusb: Add Realtek RTL8922AE VID/PID 0bda/d922 - btusb: Add Realtek RTL8922AE VID/PID 0bda/d923 - btusb: MT7922: Add VID/PID 0e8d/223c - btusb: MT7925: Add VID/PID 0e8d/8c38 - btusb: Add support for TP-Link TL-UB250 - btusb: Add Mercusys MA530 for Realtek RTL8761BUV - btusb: Add TP-Link UB600 for Realtek 8761BUV - btusb: Add support for Intel Lizard Peak 2 (0x8087:0x0040) - btusb: Add USB ID 2c4e:0128 for Mercusys MA60XNB - btusb: MT7925: Add VID/PID 13d3/3609 * tag 'for-net-next-2026-06-11' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next: (49 commits) Bluetooth: btintel_pcie: Separate coredump work from RX work Bluetooth: btmtksdio: fix infinite loop in btmtksdio_txrx_work() Bluetooth: qca: Add BT FW build version to kernel log Bluetooth: vhci: validate devcoredump state before side effects Bluetooth: L2CAP: validate connectionless PSM length Bluetooth: hci: validate codec capability element length Bluetooth: L2CAP: Fix UAF in channel timeout by holding conn ref Bluetooth: btintel_pcie: Load IOSF debug regs by controller variant Bluetooth: btintel_pcie: Add 50 ms delay before MAC init on BlazarIW Bluetooth: Add SPDX id lines to some source files Bluetooth: btintel_pcie: Add support for smart trigger dump Bluetooth: hci_h5: reset hci_uart::priv in the close() method Bluetooth: btusb: clean up probe error handling Bluetooth: btusb: fix wakeup irq devres lifetime Bluetooth: btusb: fix wakeup source leak on probe failure Bluetooth: btusb: fix use-after-free on marvell probe failure Bluetooth: btusb: fix use-after-free on registration failure Bluetooth: btmtk: fix URB leak in alloc_mtk_intr_urb error path Bluetooth: hci_core: Fix UAF in hci_unregister_dev() Bluetooth: hci_event: fix simultaneous discovery stuck in FINDING ... ==================== Link: https://patch.msgid.link/20260611183358.176776-1-luiz.dentz@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
9bbdc7dc40
69 files changed
+955
-2810
No files matched your search
@@ -77,9 +77,6 @@ CONFIG_BT_HCIUART_BCSP=y
|
||||
CONFIG_BT_HCIBCM203X=m
|
||||
CONFIG_BT_HCIBPA10X=m
|
||||
CONFIG_BT_HCIBFUSB=m
|
||||
CONFIG_BT_HCIDTL1=m
|
||||
CONFIG_BT_HCIBT3C=m
|
||||
CONFIG_BT_HCIBLUECARD=m
|
||||
CONFIG_BT_HCIBTUART=m
|
||||
CONFIG_BT_HCIVHCI=m
|
||||
CONFIG_BT_MRVL=m
|
||||
|
||||
@@ -62,9 +62,6 @@ CONFIG_BT_HCIUART_BCSP=y
|
||||
CONFIG_BT_HCIBCM203X=m
|
||||
CONFIG_BT_HCIBPA10X=m
|
||||
CONFIG_BT_HCIBFUSB=m
|
||||
CONFIG_BT_HCIDTL1=m
|
||||
CONFIG_BT_HCIBT3C=m
|
||||
CONFIG_BT_HCIBLUECARD=m
|
||||
CONFIG_BT_HCIBTUART=m
|
||||
CONFIG_BT_HCIVHCI=m
|
||||
CONFIG_PCCARD=y
|
||||
|
||||
@@ -383,9 +383,6 @@ CONFIG_BT_HCIUART_AG6XX=y
|
||||
CONFIG_BT_HCIBCM203X=m
|
||||
CONFIG_BT_HCIBPA10X=m
|
||||
CONFIG_BT_HCIBFUSB=m
|
||||
CONFIG_BT_HCIDTL1=m
|
||||
CONFIG_BT_HCIBT3C=m
|
||||
CONFIG_BT_HCIBLUECARD=m
|
||||
CONFIG_BT_HCIVHCI=m
|
||||
CONFIG_BT_MRVL=m
|
||||
CONFIG_BT_ATH3K=m
|
||||
|
||||
@@ -399,9 +399,6 @@ CONFIG_BT_HCIUART_AG6XX=y
|
||||
CONFIG_BT_HCIBCM203X=m
|
||||
CONFIG_BT_HCIBPA10X=m
|
||||
CONFIG_BT_HCIBFUSB=m
|
||||
CONFIG_BT_HCIDTL1=m
|
||||
CONFIG_BT_HCIBT3C=m
|
||||
CONFIG_BT_HCIBLUECARD=m
|
||||
CONFIG_BT_HCIVHCI=m
|
||||
CONFIG_BT_MRVL=m
|
||||
CONFIG_BT_ATH3K=m
|
||||
|
||||
@@ -184,9 +184,6 @@ CONFIG_BT_HCIUART_BCSP=y
|
||||
CONFIG_BT_HCIBCM203X=m
|
||||
CONFIG_BT_HCIBPA10X=m
|
||||
CONFIG_BT_HCIBFUSB=m
|
||||
CONFIG_BT_HCIDTL1=m
|
||||
CONFIG_BT_HCIBT3C=m
|
||||
CONFIG_BT_HCIBLUECARD=m
|
||||
CONFIG_BT_HCIVHCI=m
|
||||
CONFIG_CONNECTOR=m
|
||||
CONFIG_MTD=y
|
||||
|
||||
@@ -282,9 +282,6 @@ CONFIG_BT_HCIUART_BCSP=y
|
||||
CONFIG_BT_HCIBCM203X=m
|
||||
CONFIG_BT_HCIBPA10X=m
|
||||
CONFIG_BT_HCIBFUSB=m
|
||||
CONFIG_BT_HCIDTL1=m
|
||||
CONFIG_BT_HCIBT3C=m
|
||||
CONFIG_BT_HCIBLUECARD=m
|
||||
CONFIG_BT_HCIVHCI=m
|
||||
CONFIG_CFG80211=m
|
||||
CONFIG_MAC80211=m
|
||||
|
||||
@@ -337,46 +337,6 @@ config BT_HCIBFUSB
|
||||
Say Y here to compile support for HCI BFUSB devices into the
|
||||
kernel or say M to compile it as module (bfusb).
|
||||
|
||||
config BT_HCIDTL1
|
||||
tristate "HCI DTL1 (PC Card) driver"
|
||||
depends on PCMCIA && HAS_IOPORT
|
||||
help
|
||||
Bluetooth HCI DTL1 (PC Card) driver.
|
||||
This driver provides support for Bluetooth PCMCIA devices with
|
||||
Nokia DTL1 interface:
|
||||
Nokia Bluetooth Card
|
||||
Socket Bluetooth CF Card
|
||||
|
||||
Say Y here to compile support for HCI DTL1 devices into the
|
||||
kernel or say M to compile it as module (dtl1_cs).
|
||||
|
||||
config BT_HCIBT3C
|
||||
tristate "HCI BT3C (PC Card) driver"
|
||||
depends on PCMCIA && HAS_IOPORT
|
||||
select FW_LOADER
|
||||
help
|
||||
Bluetooth HCI BT3C (PC Card) driver.
|
||||
This driver provides support for Bluetooth PCMCIA devices with
|
||||
3Com BT3C interface:
|
||||
3Com Bluetooth Card (3CRWB6096)
|
||||
HP Bluetooth Card
|
||||
|
||||
Say Y here to compile support for HCI BT3C devices into the
|
||||
kernel or say M to compile it as module (bt3c_cs).
|
||||
|
||||
config BT_HCIBLUECARD
|
||||
tristate "HCI BlueCard (PC Card) driver"
|
||||
depends on PCMCIA && HAS_IOPORT
|
||||
help
|
||||
Bluetooth HCI BlueCard (PC Card) driver.
|
||||
This driver provides support for Bluetooth PCMCIA devices with
|
||||
Anycom BlueCard interface:
|
||||
Anycom Bluetooth PC Card
|
||||
Anycom Bluetooth CF Card
|
||||
|
||||
Say Y here to compile support for HCI BlueCard devices into the
|
||||
kernel or say M to compile it as module (bluecard_cs).
|
||||
|
||||
config BT_HCIVHCI
|
||||
tristate "HCI VHCI (Virtual HCI device) driver"
|
||||
select WANT_DEV_COREDUMP
|
||||
@@ -502,7 +462,7 @@ config BT_NXPUART
|
||||
|
||||
config BT_INTEL_PCIE
|
||||
tristate "Intel HCI PCIe driver"
|
||||
depends on PCI
|
||||
depends on PCI && ACPI
|
||||
select BT_INTEL
|
||||
select FW_LOADER
|
||||
help
|
||||
|
||||
@@ -9,9 +9,6 @@ obj-$(CONFIG_BT_HCIBCM203X) += bcm203x.o
|
||||
obj-$(CONFIG_BT_HCIBCM4377) += hci_bcm4377.o
|
||||
obj-$(CONFIG_BT_HCIBPA10X) += bpa10x.o
|
||||
obj-$(CONFIG_BT_HCIBFUSB) += bfusb.o
|
||||
obj-$(CONFIG_BT_HCIDTL1) += dtl1_cs.o
|
||||
obj-$(CONFIG_BT_HCIBT3C) += bt3c_cs.o
|
||||
obj-$(CONFIG_BT_HCIBLUECARD) += bluecard_cs.o
|
||||
|
||||
obj-$(CONFIG_BT_HCIBTUSB) += btusb.o
|
||||
obj-$(CONFIG_BT_HCIBTSDIO) += btsdio.o
|
||||
|
||||
@@ -1,908 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* Bluetooth driver for the Anycom BlueCard (LSE039/LSE041)
|
||||
*
|
||||
* Copyright (C) 2001-2002 Marcel Holtmann <marcel@holtmann.org>
|
||||
*
|
||||
*
|
||||
* 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;
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS
|
||||
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
* implied. See the License for the specific language governing
|
||||
* rights and limitations under the License.
|
||||
*
|
||||
* The initial developer of the original code is David A. Hinds
|
||||
* <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
|
||||
* are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/wait.h>
|
||||
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <pcmcia/cistpl.h>
|
||||
#include <pcmcia/ciscode.h>
|
||||
#include <pcmcia/ds.h>
|
||||
#include <pcmcia/cisreg.h>
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
|
||||
|
||||
|
||||
/* ======================== Module parameters ======================== */
|
||||
|
||||
|
||||
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
|
||||
MODULE_DESCRIPTION("Bluetooth driver for the Anycom BlueCard (LSE039/LSE041)");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
|
||||
|
||||
/* ======================== Local structures ======================== */
|
||||
|
||||
|
||||
struct bluecard_info {
|
||||
struct pcmcia_device *p_dev;
|
||||
|
||||
struct hci_dev *hdev;
|
||||
|
||||
spinlock_t lock; /* For serializing operations */
|
||||
struct timer_list timer; /* For LED control */
|
||||
|
||||
struct sk_buff_head txq;
|
||||
unsigned long tx_state;
|
||||
|
||||
unsigned long rx_state;
|
||||
unsigned long rx_count;
|
||||
struct sk_buff *rx_skb;
|
||||
|
||||
unsigned char ctrl_reg;
|
||||
unsigned long hw_state; /* Status of the hardware and LED control */
|
||||
};
|
||||
|
||||
|
||||
static int bluecard_config(struct pcmcia_device *link);
|
||||
static void bluecard_release(struct pcmcia_device *link);
|
||||
|
||||
static void bluecard_detach(struct pcmcia_device *p_dev);
|
||||
|
||||
|
||||
/* Default baud rate: 57600, 115200, 230400 or 460800 */
|
||||
#define DEFAULT_BAUD_RATE 230400
|
||||
|
||||
|
||||
/* Hardware states */
|
||||
#define CARD_READY 1
|
||||
#define CARD_ACTIVITY 2
|
||||
#define CARD_HAS_PCCARD_ID 4
|
||||
#define CARD_HAS_POWER_LED 5
|
||||
#define CARD_HAS_ACTIVITY_LED 6
|
||||
|
||||
/* Transmit states */
|
||||
#define XMIT_SENDING 1
|
||||
#define XMIT_WAKEUP 2
|
||||
#define XMIT_BUFFER_NUMBER 5 /* unset = buffer one, set = buffer two */
|
||||
#define XMIT_BUF_ONE_READY 6
|
||||
#define XMIT_BUF_TWO_READY 7
|
||||
#define XMIT_SENDING_READY 8
|
||||
|
||||
/* Receiver states */
|
||||
#define RECV_WAIT_PACKET_TYPE 0
|
||||
#define RECV_WAIT_EVENT_HEADER 1
|
||||
#define RECV_WAIT_ACL_HEADER 2
|
||||
#define RECV_WAIT_SCO_HEADER 3
|
||||
#define RECV_WAIT_DATA 4
|
||||
|
||||
/* Special packet types */
|
||||
#define PKT_BAUD_RATE_57600 0x80
|
||||
#define PKT_BAUD_RATE_115200 0x81
|
||||
#define PKT_BAUD_RATE_230400 0x82
|
||||
#define PKT_BAUD_RATE_460800 0x83
|
||||
|
||||
|
||||
/* These are the register offsets */
|
||||
#define REG_COMMAND 0x20
|
||||
#define REG_INTERRUPT 0x21
|
||||
#define REG_CONTROL 0x22
|
||||
#define REG_RX_CONTROL 0x24
|
||||
#define REG_CARD_RESET 0x30
|
||||
#define REG_LED_CTRL 0x30
|
||||
|
||||
/* REG_COMMAND */
|
||||
#define REG_COMMAND_TX_BUF_ONE 0x01
|
||||
#define REG_COMMAND_TX_BUF_TWO 0x02
|
||||
#define REG_COMMAND_RX_BUF_ONE 0x04
|
||||
#define REG_COMMAND_RX_BUF_TWO 0x08
|
||||
#define REG_COMMAND_RX_WIN_ONE 0x00
|
||||
#define REG_COMMAND_RX_WIN_TWO 0x10
|
||||
|
||||
/* REG_CONTROL */
|
||||
#define REG_CONTROL_BAUD_RATE_57600 0x00
|
||||
#define REG_CONTROL_BAUD_RATE_115200 0x01
|
||||
#define REG_CONTROL_BAUD_RATE_230400 0x02
|
||||
#define REG_CONTROL_BAUD_RATE_460800 0x03
|
||||
#define REG_CONTROL_RTS 0x04
|
||||
#define REG_CONTROL_BT_ON 0x08
|
||||
#define REG_CONTROL_BT_RESET 0x10
|
||||
#define REG_CONTROL_BT_RES_PU 0x20
|
||||
#define REG_CONTROL_INTERRUPT 0x40
|
||||
#define REG_CONTROL_CARD_RESET 0x80
|
||||
|
||||
/* REG_RX_CONTROL */
|
||||
#define RTS_LEVEL_SHIFT_BITS 0x02
|
||||
|
||||
|
||||
|
||||
/* ======================== LED handling routines ======================== */
|
||||
|
||||
|
||||
static void bluecard_activity_led_timeout(struct timer_list *t)
|
||||
{
|
||||
struct bluecard_info *info = timer_container_of(info, t, timer);
|
||||
unsigned int iobase = info->p_dev->resource[0]->start;
|
||||
|
||||
if (test_bit(CARD_ACTIVITY, &(info->hw_state))) {
|
||||
/* leave LED in inactive state for HZ/10 for blink effect */
|
||||
clear_bit(CARD_ACTIVITY, &(info->hw_state));
|
||||
mod_timer(&(info->timer), jiffies + HZ / 10);
|
||||
}
|
||||
|
||||
/* Disable activity LED, enable power LED */
|
||||
outb(0x08 | 0x20, iobase + 0x30);
|
||||
}
|
||||
|
||||
|
||||
static void bluecard_enable_activity_led(struct bluecard_info *info)
|
||||
{
|
||||
unsigned int iobase = info->p_dev->resource[0]->start;
|
||||
|
||||
/* don't disturb running blink timer */
|
||||
if (timer_pending(&(info->timer)))
|
||||
return;
|
||||
|
||||
set_bit(CARD_ACTIVITY, &(info->hw_state));
|
||||
|
||||
if (test_bit(CARD_HAS_ACTIVITY_LED, &(info->hw_state))) {
|
||||
/* Enable activity LED, keep power LED enabled */
|
||||
outb(0x18 | 0x60, iobase + 0x30);
|
||||
} else {
|
||||
/* Disable power LED */
|
||||
outb(0x00, iobase + 0x30);
|
||||
}
|
||||
|
||||
/* Stop the LED after HZ/10 */
|
||||
mod_timer(&(info->timer), jiffies + HZ / 10);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ======================== Interrupt handling ======================== */
|
||||
|
||||
|
||||
static int bluecard_write(unsigned int iobase, unsigned int offset, __u8 *buf, int len)
|
||||
{
|
||||
int i, actual;
|
||||
|
||||
actual = (len > 15) ? 15 : len;
|
||||
|
||||
outb_p(actual, iobase + offset);
|
||||
|
||||
for (i = 0; i < actual; i++)
|
||||
outb_p(buf[i], iobase + offset + i + 1);
|
||||
|
||||
return actual;
|
||||
}
|
||||
|
||||
|
||||
static void bluecard_write_wakeup(struct bluecard_info *info)
|
||||
{
|
||||
if (!info) {
|
||||
BT_ERR("Unknown device");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!test_bit(XMIT_SENDING_READY, &(info->tx_state)))
|
||||
return;
|
||||
|
||||
if (test_and_set_bit(XMIT_SENDING, &(info->tx_state))) {
|
||||
set_bit(XMIT_WAKEUP, &(info->tx_state));
|
||||
return;
|
||||
}
|
||||
|
||||
do {
|
||||
unsigned int iobase = info->p_dev->resource[0]->start;
|
||||
unsigned int offset;
|
||||
unsigned char command;
|
||||
unsigned long ready_bit;
|
||||
register struct sk_buff *skb;
|
||||
int len;
|
||||
|
||||
clear_bit(XMIT_WAKEUP, &(info->tx_state));
|
||||
|
||||
if (!pcmcia_dev_present(info->p_dev))
|
||||
return;
|
||||
|
||||
if (test_bit(XMIT_BUFFER_NUMBER, &(info->tx_state))) {
|
||||
if (!test_bit(XMIT_BUF_TWO_READY, &(info->tx_state)))
|
||||
break;
|
||||
offset = 0x10;
|
||||
command = REG_COMMAND_TX_BUF_TWO;
|
||||
ready_bit = XMIT_BUF_TWO_READY;
|
||||
} else {
|
||||
if (!test_bit(XMIT_BUF_ONE_READY, &(info->tx_state)))
|
||||
break;
|
||||
offset = 0x00;
|
||||
command = REG_COMMAND_TX_BUF_ONE;
|
||||
ready_bit = XMIT_BUF_ONE_READY;
|
||||
}
|
||||
|
||||
skb = skb_dequeue(&(info->txq));
|
||||
if (!skb)
|
||||
break;
|
||||
|
||||
if (hci_skb_pkt_type(skb) & 0x80) {
|
||||
/* Disable RTS */
|
||||
info->ctrl_reg |= REG_CONTROL_RTS;
|
||||
outb(info->ctrl_reg, iobase + REG_CONTROL);
|
||||
}
|
||||
|
||||
/* Activate LED */
|
||||
bluecard_enable_activity_led(info);
|
||||
|
||||
/* Send frame */
|
||||
len = bluecard_write(iobase, offset, skb->data, skb->len);
|
||||
|
||||
/* Tell the FPGA to send the data */
|
||||
outb_p(command, iobase + REG_COMMAND);
|
||||
|
||||
/* Mark the buffer as dirty */
|
||||
clear_bit(ready_bit, &(info->tx_state));
|
||||
|
||||
if (hci_skb_pkt_type(skb) & 0x80) {
|
||||
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
|
||||
DEFINE_WAIT(wait);
|
||||
|
||||
unsigned char baud_reg;
|
||||
|
||||
switch (hci_skb_pkt_type(skb)) {
|
||||
case PKT_BAUD_RATE_460800:
|
||||
baud_reg = REG_CONTROL_BAUD_RATE_460800;
|
||||
break;
|
||||
case PKT_BAUD_RATE_230400:
|
||||
baud_reg = REG_CONTROL_BAUD_RATE_230400;
|
||||
break;
|
||||
case PKT_BAUD_RATE_115200:
|
||||
baud_reg = REG_CONTROL_BAUD_RATE_115200;
|
||||
break;
|
||||
case PKT_BAUD_RATE_57600:
|
||||
default:
|
||||
baud_reg = REG_CONTROL_BAUD_RATE_57600;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Wait until the command reaches the baseband */
|
||||
mdelay(100);
|
||||
|
||||
/* Set baud on baseband */
|
||||
info->ctrl_reg &= ~0x03;
|
||||
info->ctrl_reg |= baud_reg;
|
||||
outb(info->ctrl_reg, iobase + REG_CONTROL);
|
||||
|
||||
/* Enable RTS */
|
||||
info->ctrl_reg &= ~REG_CONTROL_RTS;
|
||||
outb(info->ctrl_reg, iobase + REG_CONTROL);
|
||||
|
||||
/* Wait before the next HCI packet can be send */
|
||||
mdelay(1000);
|
||||
}
|
||||
|
||||
if (len == skb->len) {
|
||||
kfree_skb(skb);
|
||||
} else {
|
||||
skb_pull(skb, len);
|
||||
skb_queue_head(&(info->txq), skb);
|
||||
}
|
||||
|
||||
info->hdev->stat.byte_tx += len;
|
||||
|
||||
/* Change buffer */
|
||||
change_bit(XMIT_BUFFER_NUMBER, &(info->tx_state));
|
||||
|
||||
} while (test_bit(XMIT_WAKEUP, &(info->tx_state)));
|
||||
|
||||
clear_bit(XMIT_SENDING, &(info->tx_state));
|
||||
}
|
||||
|
||||
|
||||
static int bluecard_read(unsigned int iobase, unsigned int offset, __u8 *buf, int size)
|
||||
{
|
||||
int i, n, len;
|
||||
|
||||
outb(REG_COMMAND_RX_WIN_ONE, iobase + REG_COMMAND);
|
||||
|
||||
len = inb(iobase + offset);
|
||||
n = 0;
|
||||
i = 1;
|
||||
|
||||
while (n < len) {
|
||||
|
||||
if (i == 16) {
|
||||
outb(REG_COMMAND_RX_WIN_TWO, iobase + REG_COMMAND);
|
||||
i = 0;
|
||||
}
|
||||
|
||||
buf[n] = inb(iobase + offset + i);
|
||||
|
||||
n++;
|
||||
i++;
|
||||
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
static void bluecard_receive(struct bluecard_info *info,
|
||||
unsigned int offset)
|
||||
{
|
||||
unsigned int iobase;
|
||||
unsigned char buf[31];
|
||||
int i, len;
|
||||
|
||||
if (!info) {
|
||||
BT_ERR("Unknown device");
|
||||
return;
|
||||
}
|
||||
|
||||
iobase = info->p_dev->resource[0]->start;
|
||||
|
||||
if (test_bit(XMIT_SENDING_READY, &(info->tx_state)))
|
||||
bluecard_enable_activity_led(info);
|
||||
|
||||
len = bluecard_read(iobase, offset, buf, sizeof(buf));
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
|
||||
/* Allocate packet */
|
||||
if (!info->rx_skb) {
|
||||
info->rx_state = RECV_WAIT_PACKET_TYPE;
|
||||
info->rx_count = 0;
|
||||
info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
|
||||
if (!info->rx_skb) {
|
||||
BT_ERR("Can't allocate mem for new packet");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (info->rx_state == RECV_WAIT_PACKET_TYPE) {
|
||||
|
||||
hci_skb_pkt_type(info->rx_skb) = buf[i];
|
||||
|
||||
switch (hci_skb_pkt_type(info->rx_skb)) {
|
||||
|
||||
case 0x00:
|
||||
/* init packet */
|
||||
if (offset != 0x00) {
|
||||
set_bit(XMIT_BUF_ONE_READY, &(info->tx_state));
|
||||
set_bit(XMIT_BUF_TWO_READY, &(info->tx_state));
|
||||
set_bit(XMIT_SENDING_READY, &(info->tx_state));
|
||||
bluecard_write_wakeup(info);
|
||||
}
|
||||
|
||||
kfree_skb(info->rx_skb);
|
||||
info->rx_skb = NULL;
|
||||
break;
|
||||
|
||||
case HCI_EVENT_PKT:
|
||||
info->rx_state = RECV_WAIT_EVENT_HEADER;
|
||||
info->rx_count = HCI_EVENT_HDR_SIZE;
|
||||
break;
|
||||
|
||||
case HCI_ACLDATA_PKT:
|
||||
info->rx_state = RECV_WAIT_ACL_HEADER;
|
||||
info->rx_count = HCI_ACL_HDR_SIZE;
|
||||
break;
|
||||
|
||||
case HCI_SCODATA_PKT:
|
||||
info->rx_state = RECV_WAIT_SCO_HEADER;
|
||||
info->rx_count = HCI_SCO_HDR_SIZE;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* unknown packet */
|
||||
BT_ERR("Unknown HCI packet with type 0x%02x received",
|
||||
hci_skb_pkt_type(info->rx_skb));
|
||||
info->hdev->stat.err_rx++;
|
||||
|
||||
kfree_skb(info->rx_skb);
|
||||
info->rx_skb = NULL;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
skb_put_u8(info->rx_skb, buf[i]);
|
||||
info->rx_count--;
|
||||
|
||||
if (info->rx_count == 0) {
|
||||
|
||||
int dlen;
|
||||
struct hci_event_hdr *eh;
|
||||
struct hci_acl_hdr *ah;
|
||||
struct hci_sco_hdr *sh;
|
||||
|
||||
switch (info->rx_state) {
|
||||
|
||||
case RECV_WAIT_EVENT_HEADER:
|
||||
eh = hci_event_hdr(info->rx_skb);
|
||||
info->rx_state = RECV_WAIT_DATA;
|
||||
info->rx_count = eh->plen;
|
||||
break;
|
||||
|
||||
case RECV_WAIT_ACL_HEADER:
|
||||
ah = hci_acl_hdr(info->rx_skb);
|
||||
dlen = __le16_to_cpu(ah->dlen);
|
||||
info->rx_state = RECV_WAIT_DATA;
|
||||
info->rx_count = dlen;
|
||||
break;
|
||||
|
||||
case RECV_WAIT_SCO_HEADER:
|
||||
sh = hci_sco_hdr(info->rx_skb);
|
||||
info->rx_state = RECV_WAIT_DATA;
|
||||
info->rx_count = sh->dlen;
|
||||
break;
|
||||
|
||||
case RECV_WAIT_DATA:
|
||||
hci_recv_frame(info->hdev, info->rx_skb);
|
||||
info->rx_skb = NULL;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
info->hdev->stat.byte_rx += len;
|
||||
}
|
||||
|
||||
|
||||
static irqreturn_t bluecard_interrupt(int irq, void *dev_inst)
|
||||
{
|
||||
struct bluecard_info *info = dev_inst;
|
||||
unsigned int iobase;
|
||||
unsigned char reg;
|
||||
|
||||
if (!info || !info->hdev)
|
||||
/* our irq handler is shared */
|
||||
return IRQ_NONE;
|
||||
|
||||
if (!test_bit(CARD_READY, &(info->hw_state)))
|
||||
return IRQ_HANDLED;
|
||||
|
||||
iobase = info->p_dev->resource[0]->start;
|
||||
|
||||
spin_lock(&(info->lock));
|
||||
|
||||
/* Disable interrupt */
|
||||
info->ctrl_reg &= ~REG_CONTROL_INTERRUPT;
|
||||
outb(info->ctrl_reg, iobase + REG_CONTROL);
|
||||
|
||||
reg = inb(iobase + REG_INTERRUPT);
|
||||
|
||||
if ((reg != 0x00) && (reg != 0xff)) {
|
||||
|
||||
if (reg & 0x04) {
|
||||
bluecard_receive(info, 0x00);
|
||||
outb(0x04, iobase + REG_INTERRUPT);
|
||||
outb(REG_COMMAND_RX_BUF_ONE, iobase + REG_COMMAND);
|
||||
}
|
||||
|
||||
if (reg & 0x08) {
|
||||
bluecard_receive(info, 0x10);
|
||||
outb(0x08, iobase + REG_INTERRUPT);
|
||||
outb(REG_COMMAND_RX_BUF_TWO, iobase + REG_COMMAND);
|
||||
}
|
||||
|
||||
if (reg & 0x01) {
|
||||
set_bit(XMIT_BUF_ONE_READY, &(info->tx_state));
|
||||
outb(0x01, iobase + REG_INTERRUPT);
|
||||
bluecard_write_wakeup(info);
|
||||
}
|
||||
|
||||
if (reg & 0x02) {
|
||||
set_bit(XMIT_BUF_TWO_READY, &(info->tx_state));
|
||||
outb(0x02, iobase + REG_INTERRUPT);
|
||||
bluecard_write_wakeup(info);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Enable interrupt */
|
||||
info->ctrl_reg |= REG_CONTROL_INTERRUPT;
|
||||
outb(info->ctrl_reg, iobase + REG_CONTROL);
|
||||
|
||||
spin_unlock(&(info->lock));
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ======================== Device specific HCI commands ======================== */
|
||||
|
||||
|
||||
static int bluecard_hci_set_baud_rate(struct hci_dev *hdev, int baud)
|
||||
{
|
||||
struct bluecard_info *info = hci_get_drvdata(hdev);
|
||||
struct sk_buff *skb;
|
||||
|
||||
/* Ericsson baud rate command */
|
||||
unsigned char cmd[] = { HCI_COMMAND_PKT, 0x09, 0xfc, 0x01, 0x03 };
|
||||
|
||||
skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_KERNEL);
|
||||
if (!skb) {
|
||||
BT_ERR("Can't allocate mem for new packet");
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (baud) {
|
||||
case 460800:
|
||||
cmd[4] = 0x00;
|
||||
hci_skb_pkt_type(skb) = PKT_BAUD_RATE_460800;
|
||||
break;
|
||||
case 230400:
|
||||
cmd[4] = 0x01;
|
||||
hci_skb_pkt_type(skb) = PKT_BAUD_RATE_230400;
|
||||
break;
|
||||
case 115200:
|
||||
cmd[4] = 0x02;
|
||||
hci_skb_pkt_type(skb) = PKT_BAUD_RATE_115200;
|
||||
break;
|
||||
case 57600:
|
||||
default:
|
||||
cmd[4] = 0x03;
|
||||
hci_skb_pkt_type(skb) = PKT_BAUD_RATE_57600;
|
||||
break;
|
||||
}
|
||||
|
||||
skb_put_data(skb, cmd, sizeof(cmd));
|
||||
|
||||
skb_queue_tail(&(info->txq), skb);
|
||||
|
||||
bluecard_write_wakeup(info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ======================== HCI interface ======================== */
|
||||
|
||||
|
||||
static int bluecard_hci_flush(struct hci_dev *hdev)
|
||||
{
|
||||
struct bluecard_info *info = hci_get_drvdata(hdev);
|
||||
|
||||
/* Drop TX queue */
|
||||
skb_queue_purge(&(info->txq));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int bluecard_hci_open(struct hci_dev *hdev)
|
||||
{
|
||||
struct bluecard_info *info = hci_get_drvdata(hdev);
|
||||
unsigned int iobase = info->p_dev->resource[0]->start;
|
||||
|
||||
if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state)))
|
||||
bluecard_hci_set_baud_rate(hdev, DEFAULT_BAUD_RATE);
|
||||
|
||||
/* Enable power LED */
|
||||
outb(0x08 | 0x20, iobase + 0x30);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int bluecard_hci_close(struct hci_dev *hdev)
|
||||
{
|
||||
struct bluecard_info *info = hci_get_drvdata(hdev);
|
||||
unsigned int iobase = info->p_dev->resource[0]->start;
|
||||
|
||||
bluecard_hci_flush(hdev);
|
||||
|
||||
/* Stop LED timer */
|
||||
timer_delete_sync(&(info->timer));
|
||||
|
||||
/* Disable power LED */
|
||||
outb(0x00, iobase + 0x30);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int bluecard_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct bluecard_info *info = hci_get_drvdata(hdev);
|
||||
|
||||
switch (hci_skb_pkt_type(skb)) {
|
||||
case HCI_COMMAND_PKT:
|
||||
hdev->stat.cmd_tx++;
|
||||
break;
|
||||
case HCI_ACLDATA_PKT:
|
||||
hdev->stat.acl_tx++;
|
||||
break;
|
||||
case HCI_SCODATA_PKT:
|
||||
hdev->stat.sco_tx++;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Prepend skb with frame type */
|
||||
memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
|
||||
skb_queue_tail(&(info->txq), skb);
|
||||
|
||||
bluecard_write_wakeup(info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ======================== Card services HCI interaction ======================== */
|
||||
|
||||
|
||||
static int bluecard_open(struct bluecard_info *info)
|
||||
{
|
||||
unsigned int iobase = info->p_dev->resource[0]->start;
|
||||
struct hci_dev *hdev;
|
||||
unsigned char id;
|
||||
|
||||
spin_lock_init(&(info->lock));
|
||||
|
||||
timer_setup(&info->timer, bluecard_activity_led_timeout, 0);
|
||||
|
||||
skb_queue_head_init(&(info->txq));
|
||||
|
||||
info->rx_state = RECV_WAIT_PACKET_TYPE;
|
||||
info->rx_count = 0;
|
||||
info->rx_skb = NULL;
|
||||
|
||||
/* Initialize HCI device */
|
||||
hdev = hci_alloc_dev();
|
||||
if (!hdev) {
|
||||
BT_ERR("Can't allocate HCI device");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
info->hdev = hdev;
|
||||
|
||||
hdev->bus = HCI_PCCARD;
|
||||
hci_set_drvdata(hdev, info);
|
||||
SET_HCIDEV_DEV(hdev, &info->p_dev->dev);
|
||||
|
||||
hdev->open = bluecard_hci_open;
|
||||
hdev->close = bluecard_hci_close;
|
||||
hdev->flush = bluecard_hci_flush;
|
||||
hdev->send = bluecard_hci_send_frame;
|
||||
|
||||
id = inb(iobase + 0x30);
|
||||
|
||||
if ((id & 0x0f) == 0x02)
|
||||
set_bit(CARD_HAS_PCCARD_ID, &(info->hw_state));
|
||||
|
||||
if (id & 0x10)
|
||||
set_bit(CARD_HAS_POWER_LED, &(info->hw_state));
|
||||
|
||||
if (id & 0x20)
|
||||
set_bit(CARD_HAS_ACTIVITY_LED, &(info->hw_state));
|
||||
|
||||
/* Reset card */
|
||||
info->ctrl_reg = REG_CONTROL_BT_RESET | REG_CONTROL_CARD_RESET;
|
||||
outb(info->ctrl_reg, iobase + REG_CONTROL);
|
||||
|
||||
/* Turn FPGA off */
|
||||
outb(0x80, iobase + 0x30);
|
||||
|
||||
/* Wait some time */
|
||||
msleep(10);
|
||||
|
||||
/* Turn FPGA on */
|
||||
outb(0x00, iobase + 0x30);
|
||||
|
||||
/* Activate card */
|
||||
info->ctrl_reg = REG_CONTROL_BT_ON | REG_CONTROL_BT_RES_PU;
|
||||
outb(info->ctrl_reg, iobase + REG_CONTROL);
|
||||
|
||||
/* Enable interrupt */
|
||||
outb(0xff, iobase + REG_INTERRUPT);
|
||||
info->ctrl_reg |= REG_CONTROL_INTERRUPT;
|
||||
outb(info->ctrl_reg, iobase + REG_CONTROL);
|
||||
|
||||
if ((id & 0x0f) == 0x03) {
|
||||
/* Disable RTS */
|
||||
info->ctrl_reg |= REG_CONTROL_RTS;
|
||||
outb(info->ctrl_reg, iobase + REG_CONTROL);
|
||||
|
||||
/* Set baud rate */
|
||||
info->ctrl_reg |= 0x03;
|
||||
outb(info->ctrl_reg, iobase + REG_CONTROL);
|
||||
|
||||
/* Enable RTS */
|
||||
info->ctrl_reg &= ~REG_CONTROL_RTS;
|
||||
outb(info->ctrl_reg, iobase + REG_CONTROL);
|
||||
|
||||
set_bit(XMIT_BUF_ONE_READY, &(info->tx_state));
|
||||
set_bit(XMIT_BUF_TWO_READY, &(info->tx_state));
|
||||
set_bit(XMIT_SENDING_READY, &(info->tx_state));
|
||||
}
|
||||
|
||||
/* Start the RX buffers */
|
||||
outb(REG_COMMAND_RX_BUF_ONE, iobase + REG_COMMAND);
|
||||
outb(REG_COMMAND_RX_BUF_TWO, iobase + REG_COMMAND);
|
||||
|
||||
/* Signal that the hardware is ready */
|
||||
set_bit(CARD_READY, &(info->hw_state));
|
||||
|
||||
/* Drop TX queue */
|
||||
skb_queue_purge(&(info->txq));
|
||||
|
||||
/* Control the point at which RTS is enabled */
|
||||
outb((0x0f << RTS_LEVEL_SHIFT_BITS) | 1, iobase + REG_RX_CONTROL);
|
||||
|
||||
/* Timeout before it is safe to send the first HCI packet */
|
||||
msleep(1250);
|
||||
|
||||
/* Register HCI device */
|
||||
if (hci_register_dev(hdev) < 0) {
|
||||
BT_ERR("Can't register HCI device");
|
||||
info->hdev = NULL;
|
||||
hci_free_dev(hdev);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int bluecard_close(struct bluecard_info *info)
|
||||
{
|
||||
unsigned int iobase = info->p_dev->resource[0]->start;
|
||||
struct hci_dev *hdev = info->hdev;
|
||||
|
||||
if (!hdev)
|
||||
return -ENODEV;
|
||||
|
||||
bluecard_hci_close(hdev);
|
||||
|
||||
clear_bit(CARD_READY, &(info->hw_state));
|
||||
|
||||
/* Reset card */
|
||||
info->ctrl_reg = REG_CONTROL_BT_RESET | REG_CONTROL_CARD_RESET;
|
||||
outb(info->ctrl_reg, iobase + REG_CONTROL);
|
||||
|
||||
/* Turn FPGA off */
|
||||
outb(0x80, iobase + 0x30);
|
||||
|
||||
hci_unregister_dev(hdev);
|
||||
hci_free_dev(hdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bluecard_probe(struct pcmcia_device *link)
|
||||
{
|
||||
struct bluecard_info *info;
|
||||
|
||||
/* Create new info device */
|
||||
info = devm_kzalloc(&link->dev, sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
info->p_dev = link;
|
||||
link->priv = info;
|
||||
|
||||
link->config_flags |= CONF_ENABLE_IRQ;
|
||||
|
||||
return bluecard_config(link);
|
||||
}
|
||||
|
||||
|
||||
static void bluecard_detach(struct pcmcia_device *link)
|
||||
{
|
||||
bluecard_release(link);
|
||||
}
|
||||
|
||||
|
||||
static int bluecard_config(struct pcmcia_device *link)
|
||||
{
|
||||
struct bluecard_info *info = link->priv;
|
||||
int i, n;
|
||||
|
||||
link->config_index = 0x20;
|
||||
|
||||
link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
|
||||
link->resource[0]->end = 64;
|
||||
link->io_lines = 6;
|
||||
|
||||
for (n = 0; n < 0x400; n += 0x40) {
|
||||
link->resource[0]->start = n ^ 0x300;
|
||||
i = pcmcia_request_io(link);
|
||||
if (i == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i != 0)
|
||||
goto failed;
|
||||
|
||||
i = pcmcia_request_irq(link, bluecard_interrupt);
|
||||
if (i != 0)
|
||||
goto failed;
|
||||
|
||||
i = pcmcia_enable_device(link);
|
||||
if (i != 0)
|
||||
goto failed;
|
||||
|
||||
if (bluecard_open(info) != 0)
|
||||
goto failed;
|
||||
|
||||
return 0;
|
||||
|
||||
failed:
|
||||
bluecard_release(link);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
||||
static void bluecard_release(struct pcmcia_device *link)
|
||||
{
|
||||
struct bluecard_info *info = link->priv;
|
||||
|
||||
bluecard_close(info);
|
||||
|
||||
timer_delete_sync(&(info->timer));
|
||||
|
||||
pcmcia_disable_device(link);
|
||||
}
|
||||
|
||||
static const struct pcmcia_device_id bluecard_ids[] = {
|
||||
PCMCIA_DEVICE_PROD_ID12("BlueCard", "LSE041", 0xbaf16fbf, 0x657cc15e),
|
||||
PCMCIA_DEVICE_PROD_ID12("BTCFCARD", "LSE139", 0xe3987764, 0x2524b59c),
|
||||
PCMCIA_DEVICE_PROD_ID12("WSS", "LSE039", 0x0a0736ec, 0x24e6dfab),
|
||||
PCMCIA_DEVICE_NULL
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pcmcia, bluecard_ids);
|
||||
|
||||
static struct pcmcia_driver bluecard_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "bluecard_cs",
|
||||
.probe = bluecard_probe,
|
||||
.remove = bluecard_detach,
|
||||
.id_table = bluecard_ids,
|
||||
};
|
||||
module_pcmcia_driver(bluecard_driver);
|
||||
@@ -1,749 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* Driver for the 3Com Bluetooth PCMCIA card
|
||||
*
|
||||
* Copyright (C) 2001-2002 Marcel Holtmann <marcel@holtmann.org>
|
||||
* Jose Orlando Pereira <jop@di.uminho.pt>
|
||||
*
|
||||
*
|
||||
* 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;
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS
|
||||
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
* implied. See the License for the specific language governing
|
||||
* rights and limitations under the License.
|
||||
*
|
||||
* The initial developer of the original code is David A. Hinds
|
||||
* <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
|
||||
* are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/moduleparam.h>
|
||||
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/serial.h>
|
||||
#include <linux/serial_reg.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/firmware.h>
|
||||
|
||||
#include <pcmcia/cistpl.h>
|
||||
#include <pcmcia/ciscode.h>
|
||||
#include <pcmcia/ds.h>
|
||||
#include <pcmcia/cisreg.h>
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
|
||||
|
||||
|
||||
/* ======================== Module parameters ======================== */
|
||||
|
||||
|
||||
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
|
||||
MODULE_DESCRIPTION("Bluetooth driver for the 3Com Bluetooth PCMCIA card");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_FIRMWARE("BT3CPCC.bin");
|
||||
|
||||
|
||||
|
||||
/* ======================== Local structures ======================== */
|
||||
|
||||
|
||||
struct bt3c_info {
|
||||
struct pcmcia_device *p_dev;
|
||||
|
||||
struct hci_dev *hdev;
|
||||
|
||||
spinlock_t lock; /* For serializing operations */
|
||||
|
||||
struct sk_buff_head txq;
|
||||
unsigned long tx_state;
|
||||
|
||||
unsigned long rx_state;
|
||||
unsigned long rx_count;
|
||||
struct sk_buff *rx_skb;
|
||||
};
|
||||
|
||||
|
||||
static int bt3c_config(struct pcmcia_device *link);
|
||||
static void bt3c_release(struct pcmcia_device *link);
|
||||
|
||||
static void bt3c_detach(struct pcmcia_device *p_dev);
|
||||
|
||||
|
||||
/* Transmit states */
|
||||
#define XMIT_SENDING 1
|
||||
#define XMIT_WAKEUP 2
|
||||
#define XMIT_WAITING 8
|
||||
|
||||
/* Receiver states */
|
||||
#define RECV_WAIT_PACKET_TYPE 0
|
||||
#define RECV_WAIT_EVENT_HEADER 1
|
||||
#define RECV_WAIT_ACL_HEADER 2
|
||||
#define RECV_WAIT_SCO_HEADER 3
|
||||
#define RECV_WAIT_DATA 4
|
||||
|
||||
|
||||
|
||||
/* ======================== Special I/O functions ======================== */
|
||||
|
||||
|
||||
#define DATA_L 0
|
||||
#define DATA_H 1
|
||||
#define ADDR_L 2
|
||||
#define ADDR_H 3
|
||||
#define CONTROL 4
|
||||
|
||||
|
||||
static inline void bt3c_address(unsigned int iobase, unsigned short addr)
|
||||
{
|
||||
outb(addr & 0xff, iobase + ADDR_L);
|
||||
outb((addr >> 8) & 0xff, iobase + ADDR_H);
|
||||
}
|
||||
|
||||
|
||||
static inline void bt3c_put(unsigned int iobase, unsigned short value)
|
||||
{
|
||||
outb(value & 0xff, iobase + DATA_L);
|
||||
outb((value >> 8) & 0xff, iobase + DATA_H);
|
||||
}
|
||||
|
||||
|
||||
static inline void bt3c_io_write(unsigned int iobase, unsigned short addr, unsigned short value)
|
||||
{
|
||||
bt3c_address(iobase, addr);
|
||||
bt3c_put(iobase, value);
|
||||
}
|
||||
|
||||
|
||||
static inline unsigned short bt3c_get(unsigned int iobase)
|
||||
{
|
||||
unsigned short value = inb(iobase + DATA_L);
|
||||
|
||||
value |= inb(iobase + DATA_H) << 8;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
static inline unsigned short bt3c_read(unsigned int iobase, unsigned short addr)
|
||||
{
|
||||
bt3c_address(iobase, addr);
|
||||
|
||||
return bt3c_get(iobase);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ======================== Interrupt handling ======================== */
|
||||
|
||||
|
||||
static int bt3c_write(unsigned int iobase, int fifo_size, __u8 *buf, int len)
|
||||
{
|
||||
int actual = 0;
|
||||
|
||||
bt3c_address(iobase, 0x7080);
|
||||
|
||||
/* Fill FIFO with current frame */
|
||||
while (actual < len) {
|
||||
/* Transmit next byte */
|
||||
bt3c_put(iobase, buf[actual]);
|
||||
actual++;
|
||||
}
|
||||
|
||||
bt3c_io_write(iobase, 0x7005, actual);
|
||||
|
||||
return actual;
|
||||
}
|
||||
|
||||
|
||||
static void bt3c_write_wakeup(struct bt3c_info *info)
|
||||
{
|
||||
if (!info) {
|
||||
BT_ERR("Unknown device");
|
||||
return;
|
||||
}
|
||||
|
||||
if (test_and_set_bit(XMIT_SENDING, &(info->tx_state)))
|
||||
return;
|
||||
|
||||
do {
|
||||
unsigned int iobase = info->p_dev->resource[0]->start;
|
||||
register struct sk_buff *skb;
|
||||
int len;
|
||||
|
||||
if (!pcmcia_dev_present(info->p_dev))
|
||||
break;
|
||||
|
||||
skb = skb_dequeue(&(info->txq));
|
||||
if (!skb) {
|
||||
clear_bit(XMIT_SENDING, &(info->tx_state));
|
||||
break;
|
||||
}
|
||||
|
||||
/* Send frame */
|
||||
len = bt3c_write(iobase, 256, skb->data, skb->len);
|
||||
|
||||
if (len != skb->len)
|
||||
BT_ERR("Very strange");
|
||||
|
||||
kfree_skb(skb);
|
||||
|
||||
info->hdev->stat.byte_tx += len;
|
||||
|
||||
} while (0);
|
||||
}
|
||||
|
||||
|
||||
static void bt3c_receive(struct bt3c_info *info)
|
||||
{
|
||||
unsigned int iobase;
|
||||
int size = 0, avail;
|
||||
|
||||
if (!info) {
|
||||
BT_ERR("Unknown device");
|
||||
return;
|
||||
}
|
||||
|
||||
iobase = info->p_dev->resource[0]->start;
|
||||
|
||||
avail = bt3c_read(iobase, 0x7006);
|
||||
|
||||
bt3c_address(iobase, 0x7480);
|
||||
while (size < avail) {
|
||||
size++;
|
||||
info->hdev->stat.byte_rx++;
|
||||
|
||||
/* Allocate packet */
|
||||
if (!info->rx_skb) {
|
||||
info->rx_state = RECV_WAIT_PACKET_TYPE;
|
||||
info->rx_count = 0;
|
||||
info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
|
||||
if (!info->rx_skb) {
|
||||
BT_ERR("Can't allocate mem for new packet");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (info->rx_state == RECV_WAIT_PACKET_TYPE) {
|
||||
|
||||
hci_skb_pkt_type(info->rx_skb) = inb(iobase + DATA_L);
|
||||
inb(iobase + DATA_H);
|
||||
|
||||
switch (hci_skb_pkt_type(info->rx_skb)) {
|
||||
|
||||
case HCI_EVENT_PKT:
|
||||
info->rx_state = RECV_WAIT_EVENT_HEADER;
|
||||
info->rx_count = HCI_EVENT_HDR_SIZE;
|
||||
break;
|
||||
|
||||
case HCI_ACLDATA_PKT:
|
||||
info->rx_state = RECV_WAIT_ACL_HEADER;
|
||||
info->rx_count = HCI_ACL_HDR_SIZE;
|
||||
break;
|
||||
|
||||
case HCI_SCODATA_PKT:
|
||||
info->rx_state = RECV_WAIT_SCO_HEADER;
|
||||
info->rx_count = HCI_SCO_HDR_SIZE;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Unknown packet */
|
||||
BT_ERR("Unknown HCI packet with type 0x%02x received",
|
||||
hci_skb_pkt_type(info->rx_skb));
|
||||
info->hdev->stat.err_rx++;
|
||||
|
||||
kfree_skb(info->rx_skb);
|
||||
info->rx_skb = NULL;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
__u8 x = inb(iobase + DATA_L);
|
||||
|
||||
skb_put_u8(info->rx_skb, x);
|
||||
inb(iobase + DATA_H);
|
||||
info->rx_count--;
|
||||
|
||||
if (info->rx_count == 0) {
|
||||
|
||||
int dlen;
|
||||
struct hci_event_hdr *eh;
|
||||
struct hci_acl_hdr *ah;
|
||||
struct hci_sco_hdr *sh;
|
||||
|
||||
switch (info->rx_state) {
|
||||
|
||||
case RECV_WAIT_EVENT_HEADER:
|
||||
eh = hci_event_hdr(info->rx_skb);
|
||||
info->rx_state = RECV_WAIT_DATA;
|
||||
info->rx_count = eh->plen;
|
||||
break;
|
||||
|
||||
case RECV_WAIT_ACL_HEADER:
|
||||
ah = hci_acl_hdr(info->rx_skb);
|
||||
dlen = __le16_to_cpu(ah->dlen);
|
||||
info->rx_state = RECV_WAIT_DATA;
|
||||
info->rx_count = dlen;
|
||||
break;
|
||||
|
||||
case RECV_WAIT_SCO_HEADER:
|
||||
sh = hci_sco_hdr(info->rx_skb);
|
||||
info->rx_state = RECV_WAIT_DATA;
|
||||
info->rx_count = sh->dlen;
|
||||
break;
|
||||
|
||||
case RECV_WAIT_DATA:
|
||||
hci_recv_frame(info->hdev, info->rx_skb);
|
||||
info->rx_skb = NULL;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bt3c_io_write(iobase, 0x7006, 0x0000);
|
||||
}
|
||||
|
||||
|
||||
static irqreturn_t bt3c_interrupt(int irq, void *dev_inst)
|
||||
{
|
||||
struct bt3c_info *info = dev_inst;
|
||||
unsigned int iobase;
|
||||
int iir;
|
||||
irqreturn_t r = IRQ_NONE;
|
||||
|
||||
if (!info || !info->hdev)
|
||||
/* our irq handler is shared */
|
||||
return IRQ_NONE;
|
||||
|
||||
iobase = info->p_dev->resource[0]->start;
|
||||
|
||||
spin_lock(&(info->lock));
|
||||
|
||||
iir = inb(iobase + CONTROL);
|
||||
if (iir & 0x80) {
|
||||
int stat = bt3c_read(iobase, 0x7001);
|
||||
|
||||
if ((stat & 0xff) == 0x7f) {
|
||||
BT_ERR("Very strange (stat=0x%04x)", stat);
|
||||
} else if ((stat & 0xff) != 0xff) {
|
||||
if (stat & 0x0020) {
|
||||
int status = bt3c_read(iobase, 0x7002) & 0x10;
|
||||
bt_dev_info(info->hdev, "Antenna %s",
|
||||
status ? "out" : "in");
|
||||
}
|
||||
if (stat & 0x0001)
|
||||
bt3c_receive(info);
|
||||
if (stat & 0x0002) {
|
||||
clear_bit(XMIT_SENDING, &(info->tx_state));
|
||||
bt3c_write_wakeup(info);
|
||||
}
|
||||
|
||||
bt3c_io_write(iobase, 0x7001, 0x0000);
|
||||
|
||||
outb(iir, iobase + CONTROL);
|
||||
}
|
||||
r = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
spin_unlock(&(info->lock));
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ======================== HCI interface ======================== */
|
||||
|
||||
|
||||
static int bt3c_hci_flush(struct hci_dev *hdev)
|
||||
{
|
||||
struct bt3c_info *info = hci_get_drvdata(hdev);
|
||||
|
||||
/* Drop TX queue */
|
||||
skb_queue_purge(&(info->txq));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int bt3c_hci_open(struct hci_dev *hdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int bt3c_hci_close(struct hci_dev *hdev)
|
||||
{
|
||||
bt3c_hci_flush(hdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int bt3c_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct bt3c_info *info = hci_get_drvdata(hdev);
|
||||
unsigned long flags;
|
||||
|
||||
switch (hci_skb_pkt_type(skb)) {
|
||||
case HCI_COMMAND_PKT:
|
||||
hdev->stat.cmd_tx++;
|
||||
break;
|
||||
case HCI_ACLDATA_PKT:
|
||||
hdev->stat.acl_tx++;
|
||||
break;
|
||||
case HCI_SCODATA_PKT:
|
||||
hdev->stat.sco_tx++;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Prepend skb with frame type */
|
||||
memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
|
||||
skb_queue_tail(&(info->txq), skb);
|
||||
|
||||
spin_lock_irqsave(&(info->lock), flags);
|
||||
|
||||
bt3c_write_wakeup(info);
|
||||
|
||||
spin_unlock_irqrestore(&(info->lock), flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ======================== Card services HCI interaction ======================== */
|
||||
|
||||
|
||||
static int bt3c_load_firmware(struct bt3c_info *info,
|
||||
const unsigned char *firmware,
|
||||
int count)
|
||||
{
|
||||
char *ptr = (char *) firmware;
|
||||
char b[9];
|
||||
unsigned int iobase, tmp, tn;
|
||||
unsigned long size, addr, fcs;
|
||||
int i, err = 0;
|
||||
|
||||
iobase = info->p_dev->resource[0]->start;
|
||||
|
||||
/* Reset */
|
||||
bt3c_io_write(iobase, 0x8040, 0x0404);
|
||||
bt3c_io_write(iobase, 0x8040, 0x0400);
|
||||
|
||||
udelay(1);
|
||||
|
||||
bt3c_io_write(iobase, 0x8040, 0x0404);
|
||||
|
||||
udelay(17);
|
||||
|
||||
/* Load */
|
||||
while (count) {
|
||||
if (ptr[0] != 'S') {
|
||||
BT_ERR("Bad address in firmware");
|
||||
err = -EFAULT;
|
||||
goto error;
|
||||
}
|
||||
|
||||
memset(b, 0, sizeof(b));
|
||||
memcpy(b, ptr + 2, 2);
|
||||
if (kstrtoul(b, 16, &size) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
memset(b, 0, sizeof(b));
|
||||
memcpy(b, ptr + 4, 8);
|
||||
if (kstrtoul(b, 16, &addr) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
memset(b, 0, sizeof(b));
|
||||
memcpy(b, ptr + (size * 2) + 2, 2);
|
||||
if (kstrtoul(b, 16, &fcs) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
memset(b, 0, sizeof(b));
|
||||
for (tmp = 0, i = 0; i < size; i++) {
|
||||
memcpy(b, ptr + (i * 2) + 2, 2);
|
||||
if (kstrtouint(b, 16, &tn))
|
||||
return -EINVAL;
|
||||
tmp += tn;
|
||||
}
|
||||
|
||||
if (((tmp + fcs) & 0xff) != 0xff) {
|
||||
BT_ERR("Checksum error in firmware");
|
||||
err = -EILSEQ;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (ptr[1] == '3') {
|
||||
bt3c_address(iobase, addr);
|
||||
|
||||
memset(b, 0, sizeof(b));
|
||||
for (i = 0; i < (size - 4) / 2; i++) {
|
||||
memcpy(b, ptr + (i * 4) + 12, 4);
|
||||
if (kstrtouint(b, 16, &tmp))
|
||||
return -EINVAL;
|
||||
bt3c_put(iobase, tmp);
|
||||
}
|
||||
}
|
||||
|
||||
ptr += (size * 2) + 6;
|
||||
count -= (size * 2) + 6;
|
||||
}
|
||||
|
||||
udelay(17);
|
||||
|
||||
/* Boot */
|
||||
bt3c_address(iobase, 0x3000);
|
||||
outb(inb(iobase + CONTROL) | 0x40, iobase + CONTROL);
|
||||
|
||||
error:
|
||||
udelay(17);
|
||||
|
||||
/* Clear */
|
||||
bt3c_io_write(iobase, 0x7006, 0x0000);
|
||||
bt3c_io_write(iobase, 0x7005, 0x0000);
|
||||
bt3c_io_write(iobase, 0x7001, 0x0000);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
static int bt3c_open(struct bt3c_info *info)
|
||||
{
|
||||
const struct firmware *firmware;
|
||||
struct hci_dev *hdev;
|
||||
int err;
|
||||
|
||||
spin_lock_init(&(info->lock));
|
||||
|
||||
skb_queue_head_init(&(info->txq));
|
||||
|
||||
info->rx_state = RECV_WAIT_PACKET_TYPE;
|
||||
info->rx_count = 0;
|
||||
info->rx_skb = NULL;
|
||||
|
||||
/* Initialize HCI device */
|
||||
hdev = hci_alloc_dev();
|
||||
if (!hdev) {
|
||||
BT_ERR("Can't allocate HCI device");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
info->hdev = hdev;
|
||||
|
||||
hdev->bus = HCI_PCCARD;
|
||||
hci_set_drvdata(hdev, info);
|
||||
SET_HCIDEV_DEV(hdev, &info->p_dev->dev);
|
||||
|
||||
hdev->open = bt3c_hci_open;
|
||||
hdev->close = bt3c_hci_close;
|
||||
hdev->flush = bt3c_hci_flush;
|
||||
hdev->send = bt3c_hci_send_frame;
|
||||
|
||||
/* Load firmware */
|
||||
err = request_firmware(&firmware, "BT3CPCC.bin", &info->p_dev->dev);
|
||||
if (err < 0) {
|
||||
BT_ERR("Firmware request failed");
|
||||
goto error;
|
||||
}
|
||||
|
||||
err = bt3c_load_firmware(info, firmware->data, firmware->size);
|
||||
|
||||
release_firmware(firmware);
|
||||
|
||||
if (err < 0) {
|
||||
BT_ERR("Firmware loading failed");
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Timeout before it is safe to send the first HCI packet */
|
||||
msleep(1000);
|
||||
|
||||
/* Register HCI device */
|
||||
err = hci_register_dev(hdev);
|
||||
if (err < 0) {
|
||||
BT_ERR("Can't register HCI device");
|
||||
goto error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
info->hdev = NULL;
|
||||
hci_free_dev(hdev);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
static int bt3c_close(struct bt3c_info *info)
|
||||
{
|
||||
struct hci_dev *hdev = info->hdev;
|
||||
|
||||
if (!hdev)
|
||||
return -ENODEV;
|
||||
|
||||
bt3c_hci_close(hdev);
|
||||
|
||||
hci_unregister_dev(hdev);
|
||||
hci_free_dev(hdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bt3c_probe(struct pcmcia_device *link)
|
||||
{
|
||||
struct bt3c_info *info;
|
||||
|
||||
/* Create new info device */
|
||||
info = devm_kzalloc(&link->dev, sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
info->p_dev = link;
|
||||
link->priv = info;
|
||||
|
||||
link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_VPP |
|
||||
CONF_AUTO_SET_IO;
|
||||
|
||||
return bt3c_config(link);
|
||||
}
|
||||
|
||||
|
||||
static void bt3c_detach(struct pcmcia_device *link)
|
||||
{
|
||||
bt3c_release(link);
|
||||
}
|
||||
|
||||
static int bt3c_check_config(struct pcmcia_device *p_dev, void *priv_data)
|
||||
{
|
||||
int *try = priv_data;
|
||||
|
||||
if (!try)
|
||||
p_dev->io_lines = 16;
|
||||
|
||||
if ((p_dev->resource[0]->end != 8) || (p_dev->resource[0]->start == 0))
|
||||
return -EINVAL;
|
||||
|
||||
p_dev->resource[0]->end = 8;
|
||||
p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
|
||||
p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
|
||||
|
||||
return pcmcia_request_io(p_dev);
|
||||
}
|
||||
|
||||
static int bt3c_check_config_notpicky(struct pcmcia_device *p_dev,
|
||||
void *priv_data)
|
||||
{
|
||||
static unsigned int base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 };
|
||||
int j;
|
||||
|
||||
if (p_dev->io_lines > 3)
|
||||
return -ENODEV;
|
||||
|
||||
p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
|
||||
p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
|
||||
p_dev->resource[0]->end = 8;
|
||||
|
||||
for (j = 0; j < 5; j++) {
|
||||
p_dev->resource[0]->start = base[j];
|
||||
p_dev->io_lines = base[j] ? 16 : 3;
|
||||
if (!pcmcia_request_io(p_dev))
|
||||
return 0;
|
||||
}
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int bt3c_config(struct pcmcia_device *link)
|
||||
{
|
||||
struct bt3c_info *info = link->priv;
|
||||
int i;
|
||||
unsigned long try;
|
||||
|
||||
/* First pass: look for a config entry that looks normal.
|
||||
* Two tries: without IO aliases, then with aliases
|
||||
*/
|
||||
for (try = 0; try < 2; try++)
|
||||
if (!pcmcia_loop_config(link, bt3c_check_config, (void *) try))
|
||||
goto found_port;
|
||||
|
||||
/* Second pass: try to find an entry that isn't picky about
|
||||
* its base address, then try to grab any standard serial port
|
||||
* address, and finally try to get any free port.
|
||||
*/
|
||||
if (!pcmcia_loop_config(link, bt3c_check_config_notpicky, NULL))
|
||||
goto found_port;
|
||||
|
||||
BT_ERR("No usable port range found");
|
||||
goto failed;
|
||||
|
||||
found_port:
|
||||
i = pcmcia_request_irq(link, &bt3c_interrupt);
|
||||
if (i != 0)
|
||||
goto failed;
|
||||
|
||||
i = pcmcia_enable_device(link);
|
||||
if (i != 0)
|
||||
goto failed;
|
||||
|
||||
if (bt3c_open(info) != 0)
|
||||
goto failed;
|
||||
|
||||
return 0;
|
||||
|
||||
failed:
|
||||
bt3c_release(link);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
||||
static void bt3c_release(struct pcmcia_device *link)
|
||||
{
|
||||
struct bt3c_info *info = link->priv;
|
||||
|
||||
bt3c_close(info);
|
||||
|
||||
pcmcia_disable_device(link);
|
||||
}
|
||||
|
||||
|
||||
static const struct pcmcia_device_id bt3c_ids[] = {
|
||||
PCMCIA_DEVICE_PROD_ID13("3COM", "Bluetooth PC Card", 0xefce0a31, 0xd4ce9b02),
|
||||
PCMCIA_DEVICE_NULL
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pcmcia, bt3c_ids);
|
||||
|
||||
static struct pcmcia_driver bt3c_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "bt3c_cs",
|
||||
.probe = bt3c_probe,
|
||||
.remove = bt3c_detach,
|
||||
.id_table = bt3c_ids,
|
||||
};
|
||||
module_pcmcia_driver(bt3c_driver);
|
||||
@@ -67,9 +67,10 @@ static struct {
|
||||
u32 fw_build_num;
|
||||
} coredump_info;
|
||||
|
||||
static const guid_t btintel_guid_dsm =
|
||||
const guid_t btintel_guid_dsm =
|
||||
GUID_INIT(0xaa10f4e0, 0x81ac, 0x4233,
|
||||
0xab, 0xf6, 0x3b, 0x2a, 0xc5, 0x0e, 0x28, 0xd9);
|
||||
EXPORT_SYMBOL_GPL(btintel_guid_dsm);
|
||||
|
||||
int btintel_check_bdaddr(struct hci_dev *hdev)
|
||||
{
|
||||
@@ -2624,7 +2625,7 @@ static void btintel_set_ppag(struct hci_dev *hdev, struct intel_version_tlv *ver
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
static int btintel_acpi_reset_method(struct hci_dev *hdev)
|
||||
int btintel_acpi_reset_method(struct hci_dev *hdev)
|
||||
{
|
||||
int ret = 0;
|
||||
acpi_status status;
|
||||
@@ -2632,14 +2633,14 @@ static int btintel_acpi_reset_method(struct hci_dev *hdev)
|
||||
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
|
||||
status = acpi_evaluate_object(ACPI_HANDLE(GET_HCIDEV_DEV(hdev)), "_PRR", NULL, &buffer);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
if (ACPI_FAILURE(status) || !buffer.pointer) {
|
||||
bt_dev_err(hdev, "Failed to run _PRR method");
|
||||
ret = -ENODEV;
|
||||
return ret;
|
||||
}
|
||||
p = buffer.pointer;
|
||||
|
||||
if (p->package.count != 1 || p->type != ACPI_TYPE_PACKAGE) {
|
||||
if (p->type != ACPI_TYPE_PACKAGE || p->package.count != 1) {
|
||||
bt_dev_err(hdev, "Invalid arguments");
|
||||
ret = -EINVAL;
|
||||
goto exit_on_error;
|
||||
@@ -2663,6 +2664,7 @@ exit_on_error:
|
||||
kfree(buffer.pointer);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(btintel_acpi_reset_method);
|
||||
|
||||
static void btintel_set_dsm_reset_method(struct hci_dev *hdev,
|
||||
struct intel_version_tlv *ver_tlv)
|
||||
|
||||
@@ -53,6 +53,7 @@ struct intel_tlv {
|
||||
} __packed;
|
||||
|
||||
#define BTINTEL_HCI_OP_RESET 0xfc01
|
||||
#define BTINTEL_HCI_OP_DEBUG 0xfcd9
|
||||
|
||||
#define BTINTEL_CNVI_BLAZARI 0x900 /* BlazarI - Lunar Lake */
|
||||
#define BTINTEL_CNVI_BLAZARIW 0x901 /* BlazarIW - Wildcat Lake */
|
||||
@@ -77,8 +78,11 @@ struct intel_tlv {
|
||||
#define BTINTEL_HWID_BZRU 0x1d /* BlazarU - Meteor Lake */
|
||||
#define BTINTEL_HWID_SCP 0x1f /* Scorpius Peak - Panther Lake */
|
||||
#define BTINTEL_HWID_SCP2 0x20 /* Scorpius Peak2 - Nova Lake */
|
||||
#define BTINTEL_HWID_SCP2F 0x21 /* Scorpius Peak2-F - Nova Lake */
|
||||
#define BTINTEL_HWID_BZRIW 0x22 /* BlazarIW - Wildcat Lake */
|
||||
|
||||
extern const guid_t btintel_guid_dsm;
|
||||
|
||||
struct intel_version_tlv {
|
||||
u32 cnvi_top;
|
||||
u32 cnvr_top;
|
||||
@@ -289,6 +293,7 @@ int btintel_bootloader_setup_tlv(struct hci_dev *hdev,
|
||||
int btintel_shutdown_combined(struct hci_dev *hdev);
|
||||
void btintel_hw_error(struct hci_dev *hdev, u8 code);
|
||||
void btintel_print_fseq_info(struct hci_dev *hdev);
|
||||
int btintel_acpi_reset_method(struct hci_dev *hdev);
|
||||
#else
|
||||
|
||||
static inline int btintel_check_bdaddr(struct hci_dev *hdev)
|
||||
@@ -422,4 +427,8 @@ static inline void btintel_hw_error(struct hci_dev *hdev, u8 code)
|
||||
static inline void btintel_print_fseq_info(struct hci_dev *hdev)
|
||||
{
|
||||
}
|
||||
static inline int btintel_acpi_reset_method(struct hci_dev *hdev)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
#endif
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <linux/wait.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
#include <linux/unaligned.h>
|
||||
#include <linux/devcoredump.h>
|
||||
@@ -102,6 +103,22 @@ enum {
|
||||
BTINTEL_PCIE_D3
|
||||
};
|
||||
|
||||
enum {
|
||||
BTINTEL_PCIE_DSM_SET_RESET_TIMING = 1,
|
||||
BTINTEL_PCIE_DSM_GET_RESET_TIMING = 2,
|
||||
BTINTEL_PCIE_DSM_BT_PLDR_CONFIG = 3,
|
||||
BTINTEL_PCIE_DSM_GET_RESET_TYPE = 4,
|
||||
BTINTEL_PCIE_DSM_DYNAMIC_PLDR = 5,
|
||||
BTINTEL_PCIE_DSM_GET_RESET_METHOD = 6,
|
||||
BTINTEL_PCIE_DSM_SET_PLDR_DELAY = 7,
|
||||
};
|
||||
|
||||
enum btintel_dsm_internal_product_reset_mode {
|
||||
BTINTEL_PCIE_DSM_PLDR_MODE_EN_PROD_RESET = BIT(0),
|
||||
BTINTEL_PCIE_DSM_PLDR_MODE_EN_WIFI_FLR = BIT(1),
|
||||
BTINTEL_PCIE_DSM_PLDR_MODE_EN_BT_OFF_ON = BIT(2),
|
||||
};
|
||||
|
||||
/* Structure for dbgc fragment buffer
|
||||
* @buf_addr_lsb: LSB of the buffer's physical address
|
||||
* @buf_addr_msb: MSB of the buffer's physical address
|
||||
@@ -128,10 +145,21 @@ struct btintel_pcie_dbgc_ctxt {
|
||||
struct btintel_pcie_dbgc_ctxt_buf bufs[BTINTEL_PCIE_DBGC_BUFFER_COUNT];
|
||||
};
|
||||
|
||||
struct btintel_pcie_removal {
|
||||
struct pci_dev *pdev;
|
||||
struct work_struct work;
|
||||
};
|
||||
struct btintel_pcie_trigger_evt {
|
||||
u8 type;
|
||||
u8 len;
|
||||
__le32 addr;
|
||||
__le32 size;
|
||||
} __packed;
|
||||
|
||||
struct btintel_pcie_fwtrigger_evt {
|
||||
__le32 reserved;
|
||||
u8 type; /* Debug Trigger event */
|
||||
__le16 len;
|
||||
u8 event_type;
|
||||
__le16 event_id;
|
||||
__le16 reserved2;
|
||||
} __packed;
|
||||
|
||||
static LIST_HEAD(btintel_pcie_recovery_list);
|
||||
static DEFINE_SPINLOCK(btintel_pcie_recovery_lock);
|
||||
@@ -626,9 +654,10 @@ static void *btintel_pcie_copy_tlv(void *dest, enum btintel_pcie_tlv_type type,
|
||||
static int btintel_pcie_read_dram_buffers(struct btintel_pcie_data *data)
|
||||
{
|
||||
u32 offset, prev_size, wr_ptr_status, dump_size, data_len;
|
||||
u32 status_reg, wrap_reg;
|
||||
struct btintel_pcie_dbgc *dbgc = &data->dbgc;
|
||||
struct hci_dev *hdev = data->hdev;
|
||||
u8 *pdata, *p, buf_idx;
|
||||
u8 *pdata, *p, buf_idx, hw_variant;
|
||||
struct intel_tlv *tlv;
|
||||
struct timespec64 now;
|
||||
struct tm tm_now;
|
||||
@@ -641,7 +670,28 @@ static int btintel_pcie_read_dram_buffers(struct btintel_pcie_data *data)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
|
||||
wr_ptr_status = btintel_pcie_rd_dev_mem(data, BTINTEL_PCIE_DBGC_CUR_DBGBUFF_STATUS);
|
||||
hw_variant = INTEL_HW_VARIANT(data->cnvi);
|
||||
switch (hw_variant) {
|
||||
case BTINTEL_HWID_BZRI:
|
||||
case BTINTEL_HWID_BZRIW:
|
||||
status_reg = BTINTEL_PCIE_DBGC_CUR_DBGBUFF_STATUS;
|
||||
wrap_reg = BTINTEL_PCIE_DBGC_DBGBUFF_WRAP_ARND;
|
||||
break;
|
||||
case BTINTEL_HWID_SCP:
|
||||
case BTINTEL_HWID_SCP2:
|
||||
case BTINTEL_HWID_SCP2F:
|
||||
status_reg = BTINTEL_PCIE_DBGC_CUR_DBGBUFF_STATUS_SCP;
|
||||
wrap_reg = BTINTEL_PCIE_DBGC_DBGBUFF_WRAP_ARND_SCP;
|
||||
break;
|
||||
default:
|
||||
bt_dev_err(hdev, "Unsupported Intel hardware variant (0x%2.2x)",
|
||||
hw_variant);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
wr_ptr_status = btintel_pcie_rd_dev_mem(data, status_reg);
|
||||
data->dmp_hdr.wrap_ctr = btintel_pcie_rd_dev_mem(data, wrap_reg);
|
||||
|
||||
offset = wr_ptr_status & BTINTEL_PCIE_DBG_OFFSET_BIT_MASK;
|
||||
|
||||
buf_idx = BTINTEL_PCIE_DBGC_DBG_BUF_IDX(wr_ptr_status);
|
||||
@@ -684,6 +734,11 @@ static int btintel_pcie_read_dram_buffers(struct btintel_pcie_data *data)
|
||||
sizeof(*tlv) + strlen(vendor) +
|
||||
sizeof(*tlv) + strlen(driver);
|
||||
|
||||
if (data->dmp_hdr.event_type && data->dmp_hdr.event_id) {
|
||||
data_len += sizeof(*tlv) + sizeof(data->dmp_hdr.event_type);
|
||||
data_len += sizeof(*tlv) + sizeof(data->dmp_hdr.event_id);
|
||||
}
|
||||
|
||||
/*
|
||||
* sizeof(u32) - signature
|
||||
* sizeof(data_len) - to store tlv data size
|
||||
@@ -718,10 +773,6 @@ static int btintel_pcie_read_dram_buffers(struct btintel_pcie_data *data)
|
||||
sizeof(data->dmp_hdr.write_ptr));
|
||||
p = btintel_pcie_copy_tlv(p, BTINTEL_WRAP_CTR, &data->dmp_hdr.wrap_ctr,
|
||||
sizeof(data->dmp_hdr.wrap_ctr));
|
||||
|
||||
data->dmp_hdr.wrap_ctr = btintel_pcie_rd_dev_mem(data,
|
||||
BTINTEL_PCIE_DBGC_DBGBUFF_WRAP_ARND);
|
||||
|
||||
p = btintel_pcie_copy_tlv(p, BTINTEL_TRIGGER_REASON, &data->dmp_hdr.trigger_reason,
|
||||
sizeof(data->dmp_hdr.trigger_reason));
|
||||
p = btintel_pcie_copy_tlv(p, BTINTEL_FW_SHA, &data->dmp_hdr.fw_git_sha1,
|
||||
@@ -731,6 +782,17 @@ static int btintel_pcie_read_dram_buffers(struct btintel_pcie_data *data)
|
||||
p = btintel_pcie_copy_tlv(p, BTINTEL_CNVI_TOP, &data->dmp_hdr.cnvi_top,
|
||||
sizeof(data->dmp_hdr.cnvi_top));
|
||||
|
||||
if (data->dmp_hdr.event_type && data->dmp_hdr.event_id) {
|
||||
p = btintel_pcie_copy_tlv(p, BTINTEL_EVENT_TYPE,
|
||||
&data->dmp_hdr.event_type,
|
||||
sizeof(data->dmp_hdr.event_type));
|
||||
p = btintel_pcie_copy_tlv(p, BTINTEL_EVENT_ID,
|
||||
&data->dmp_hdr.event_id,
|
||||
sizeof(data->dmp_hdr.event_id));
|
||||
data->dmp_hdr.event_type = 0;
|
||||
data->dmp_hdr.event_id = 0;
|
||||
}
|
||||
|
||||
memcpy(p, dbgc->bufs[0].data, dbgc->count * BTINTEL_PCIE_DBGC_BUFFER_SIZE);
|
||||
dev_coredumpv(&hdev->dev, pdata, dump_size, GFP_KERNEL);
|
||||
return 0;
|
||||
@@ -755,6 +817,11 @@ static void btintel_pcie_dump_traces(struct hci_dev *hdev)
|
||||
bt_dev_err(hdev, "Failed to dump traces: (%d)", ret);
|
||||
}
|
||||
|
||||
static bool btintel_pcie_is_blazariw(struct pci_dev *pdev)
|
||||
{
|
||||
return pdev->device == 0x4D76;
|
||||
}
|
||||
|
||||
/* This function enables BT function by setting BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_INIT bit in
|
||||
* BTINTEL_PCIE_CSR_FUNC_CTRL_REG register and wait for MSI-X with
|
||||
* BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0.
|
||||
@@ -774,6 +841,14 @@ static int btintel_pcie_enable_bt(struct btintel_pcie_data *data)
|
||||
btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_CI_ADDR_MSB_REG,
|
||||
(u64)data->ci_p_addr >> 32);
|
||||
|
||||
/* On BlazarIW, the D0 entry to MAC init does not complete in
|
||||
* time. Wait 50 ms (worst case as per HW analysis) for the
|
||||
* shared hardware reset flow to complete before proceeding with
|
||||
* MAC init.
|
||||
*/
|
||||
if (btintel_pcie_is_blazariw(data->pdev))
|
||||
msleep(50);
|
||||
|
||||
/* Reset the cached value of boot stage. it is updated by the MSI-X
|
||||
* gp0 interrupt handler.
|
||||
*/
|
||||
@@ -1318,6 +1393,73 @@ exit_on_error:
|
||||
kfree(buf);
|
||||
}
|
||||
|
||||
static int btintel_pcie_dump_fwtrigger_event(struct btintel_pcie_data *data)
|
||||
{
|
||||
struct btintel_pcie_fwtrigger_evt *evt;
|
||||
struct sk_buff *skb;
|
||||
unsigned int len;
|
||||
int err;
|
||||
u8 *buf;
|
||||
|
||||
if (!data->debug_evt_size || !data->debug_evt_addr)
|
||||
return -EINVAL;
|
||||
|
||||
len = data->debug_evt_size;
|
||||
|
||||
len = ALIGN_DOWN(len, 4);
|
||||
|
||||
if (len < sizeof(*evt) || len > HCI_MAX_EVENT_SIZE) {
|
||||
bt_dev_err(data->hdev, "Invalid FW trigger data size (%u bytes)", len);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
buf = kzalloc(len, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
btintel_pcie_mac_init(data);
|
||||
|
||||
err = btintel_pcie_read_device_mem(data, buf, data->debug_evt_addr,
|
||||
len);
|
||||
if (err)
|
||||
goto exit_on_error;
|
||||
|
||||
evt = (void *)buf;
|
||||
data->dmp_hdr.event_type = evt->event_type;
|
||||
data->dmp_hdr.event_id = le16_to_cpu(evt->event_id);
|
||||
|
||||
bt_dev_dbg(data->hdev, "event type: 0x%2.2x event id: 0x%4.4x len: %u",
|
||||
data->dmp_hdr.event_type, data->dmp_hdr.event_id, len);
|
||||
|
||||
skb = bt_skb_alloc(len, GFP_KERNEL);
|
||||
if (!skb) {
|
||||
err = -ENOMEM;
|
||||
goto exit_on_error;
|
||||
}
|
||||
skb_put_data(skb, buf, len);
|
||||
|
||||
hci_recv_diag(data->hdev, skb);
|
||||
err = 0;
|
||||
|
||||
exit_on_error:
|
||||
kfree(buf);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void btintel_pcie_msix_fw_trigger_handler(struct btintel_pcie_data *data)
|
||||
{
|
||||
bt_dev_dbg(data->hdev, "Received firmware smart trigger cause");
|
||||
|
||||
if (test_and_set_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, &data->flags))
|
||||
return;
|
||||
|
||||
/* Trigger device core dump when there is FW assert */
|
||||
if (!test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags))
|
||||
data->dmp_hdr.trigger_reason = BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT;
|
||||
|
||||
queue_work(data->coredump_workqueue, &data->coredump_work);
|
||||
}
|
||||
|
||||
static void btintel_pcie_msix_hw_exp_handler(struct btintel_pcie_data *data)
|
||||
{
|
||||
bt_dev_err(data->hdev, "Received hw exception interrupt");
|
||||
@@ -1332,14 +1474,27 @@ static void btintel_pcie_msix_hw_exp_handler(struct btintel_pcie_data *data)
|
||||
if (!test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags))
|
||||
data->dmp_hdr.trigger_reason = BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT;
|
||||
|
||||
queue_work(data->workqueue, &data->rx_work);
|
||||
queue_work(data->coredump_workqueue, &data->coredump_work);
|
||||
}
|
||||
|
||||
static void btintel_pcie_rx_work(struct work_struct *work)
|
||||
static void btintel_pcie_coredump_worker(struct work_struct *work)
|
||||
{
|
||||
struct btintel_pcie_data *data = container_of(work,
|
||||
struct btintel_pcie_data, rx_work);
|
||||
struct sk_buff *skb;
|
||||
struct btintel_pcie_data, coredump_work);
|
||||
int err;
|
||||
|
||||
/* hdev is NULL until setup_hdev() succeeds, and is cleared on
|
||||
* teardown after disable_work_sync() drains us; bail in that case.
|
||||
*/
|
||||
if (!data->hdev)
|
||||
return;
|
||||
|
||||
if (test_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, &data->flags)) {
|
||||
err = btintel_pcie_dump_fwtrigger_event(data);
|
||||
if (err)
|
||||
bt_dev_warn(data->hdev, "failed to log fwtrigger event");
|
||||
clear_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, &data->flags);
|
||||
}
|
||||
|
||||
if (test_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags)) {
|
||||
btintel_pcie_dump_traces(data->hdev);
|
||||
@@ -1357,6 +1512,13 @@ static void btintel_pcie_rx_work(struct work_struct *work)
|
||||
btintel_pcie_read_hwexp(data);
|
||||
clear_bit(BTINTEL_PCIE_HWEXP_INPROGRESS, &data->flags);
|
||||
}
|
||||
}
|
||||
|
||||
static void btintel_pcie_rx_work(struct work_struct *work)
|
||||
{
|
||||
struct btintel_pcie_data *data = container_of(work,
|
||||
struct btintel_pcie_data, rx_work);
|
||||
struct sk_buff *skb;
|
||||
|
||||
/* Process the sk_buf in queue and send to the HCI layer */
|
||||
while ((skb = skb_dequeue(&data->rx_skb_q))) {
|
||||
@@ -1497,6 +1659,9 @@ static irqreturn_t btintel_pcie_irq_msix_handler(int irq, void *dev_id)
|
||||
btintel_pcie_msix_tx_handle(data);
|
||||
}
|
||||
|
||||
if (intr_hw & BTINTEL_PCIE_MSIX_HW_INT_CAUSES_FWTRIG)
|
||||
btintel_pcie_msix_fw_trigger_handler(data);
|
||||
|
||||
/* This interrupt is triggered by the firmware after updating
|
||||
* boot_stage register and image_response register
|
||||
*/
|
||||
@@ -1571,6 +1736,7 @@ static struct btintel_pcie_causes_list causes_list[] = {
|
||||
{ BTINTEL_PCIE_MSIX_FH_INT_CAUSES_1, BTINTEL_PCIE_CSR_MSIX_FH_INT_MASK, 0x01 },
|
||||
{ BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0, BTINTEL_PCIE_CSR_MSIX_HW_INT_MASK, 0x20 },
|
||||
{ BTINTEL_PCIE_MSIX_HW_INT_CAUSES_HWEXP, BTINTEL_PCIE_CSR_MSIX_HW_INT_MASK, 0x23 },
|
||||
{ BTINTEL_PCIE_MSIX_HW_INT_CAUSES_FWTRIG, BTINTEL_PCIE_CSR_MSIX_HW_INT_MASK, 0x25 },
|
||||
};
|
||||
|
||||
/* This function configures the interrupt masks for both HW_INT_CAUSES and
|
||||
@@ -2027,9 +2193,11 @@ exit_error:
|
||||
|
||||
static void btintel_pcie_release_hdev(struct btintel_pcie_data *data)
|
||||
{
|
||||
struct hci_dev *hdev;
|
||||
struct hci_dev *hdev = data->hdev;
|
||||
|
||||
if (!hdev)
|
||||
return;
|
||||
|
||||
hdev = data->hdev;
|
||||
hci_unregister_dev(hdev);
|
||||
hci_free_dev(hdev);
|
||||
data->hdev = NULL;
|
||||
@@ -2057,6 +2225,55 @@ static void btintel_pcie_synchronize_irqs(struct btintel_pcie_data *data)
|
||||
synchronize_irq(data->msix_entries[i].vector);
|
||||
}
|
||||
|
||||
static int btintel_pcie_get_debug_info_addr(struct hci_dev *hdev)
|
||||
{
|
||||
struct btintel_pcie_data *data = hci_get_drvdata(hdev);
|
||||
struct btintel_pcie_trigger_evt *evt;
|
||||
u8 param[1] = {0x10};
|
||||
struct sk_buff *skb;
|
||||
int err = 0;
|
||||
|
||||
skb = __hci_cmd_sync(hdev, BTINTEL_HCI_OP_DEBUG, 1, param,
|
||||
HCI_CMD_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
bt_dev_err(hdev, "Reading Intel read debug info address command failed (%ld)",
|
||||
PTR_ERR(skb));
|
||||
/* Not all Intel products supports this command */
|
||||
if (PTR_ERR(skb) == -EOPNOTSUPP)
|
||||
return 0;
|
||||
return PTR_ERR(skb);
|
||||
}
|
||||
|
||||
if (skb->len < (1 + sizeof(*evt))) {
|
||||
bt_dev_err(hdev, "Debug info response too short (%u bytes)", skb->len);
|
||||
err = -EIO;
|
||||
goto exit_error;
|
||||
}
|
||||
|
||||
/* Check the status */
|
||||
if (skb->data[0]) {
|
||||
bt_dev_err(hdev, "Reading Intel read debug info command failed (0x%2.2x)",
|
||||
skb->data[0]);
|
||||
err = -EIO;
|
||||
goto exit_error;
|
||||
}
|
||||
|
||||
/* Consume Command Complete Status field */
|
||||
skb_pull(skb, 1);
|
||||
|
||||
evt = (void *)skb->data;
|
||||
|
||||
data->debug_evt_addr = le32_to_cpu(evt->addr);
|
||||
data->debug_evt_size = le32_to_cpu(evt->size);
|
||||
|
||||
bt_dev_dbg(hdev, "config type: %u config len: %u debug event addr: 0x%8.8x size: 0x%8.8x",
|
||||
evt->type, evt->len, data->debug_evt_addr,
|
||||
data->debug_evt_size);
|
||||
exit_error:
|
||||
kfree_skb(skb);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int btintel_pcie_setup_internal(struct hci_dev *hdev)
|
||||
{
|
||||
struct btintel_pcie_data *data = hci_get_drvdata(hdev);
|
||||
@@ -2156,6 +2373,10 @@ static int btintel_pcie_setup_internal(struct hci_dev *hdev)
|
||||
if (ver_tlv.img_type == 0x02 || ver_tlv.img_type == 0x03)
|
||||
data->dmp_hdr.fw_git_sha1 = ver_tlv.git_sha1;
|
||||
|
||||
err = btintel_pcie_get_debug_info_addr(hdev);
|
||||
if (err)
|
||||
goto exit_error;
|
||||
|
||||
btintel_print_fseq_info(hdev);
|
||||
exit_error:
|
||||
kfree_skb(skb);
|
||||
@@ -2265,34 +2486,167 @@ static void btintel_pcie_inc_recovery_count(struct pci_dev *pdev,
|
||||
}
|
||||
|
||||
static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data);
|
||||
static void btintel_pcie_reset(struct hci_dev *hdev);
|
||||
|
||||
static void btintel_pcie_removal_work(struct work_struct *wk)
|
||||
static int btintel_pcie_acpi_reset_method(struct btintel_pcie_data *data)
|
||||
{
|
||||
struct btintel_pcie_removal *removal =
|
||||
container_of(wk, struct btintel_pcie_removal, work);
|
||||
struct pci_dev *pdev = removal->pdev;
|
||||
struct btintel_pcie_data *data;
|
||||
union acpi_object *obj, argv4;
|
||||
acpi_handle handle;
|
||||
int ret;
|
||||
struct pldr_mode {
|
||||
__le16 cmd_type;
|
||||
__le16 cmd_payload;
|
||||
} __packed;
|
||||
|
||||
/* set 1 for _PRR mode
|
||||
* Product Reset (PLDR Abort flow)
|
||||
*/
|
||||
static const struct pldr_mode mode = {
|
||||
.cmd_type = cpu_to_le16(1),
|
||||
.cmd_payload = cpu_to_le16(BTINTEL_PCIE_DSM_PLDR_MODE_EN_PROD_RESET |
|
||||
BTINTEL_PCIE_DSM_PLDR_MODE_EN_WIFI_FLR),
|
||||
};
|
||||
struct hci_dev *hdev = data->hdev;
|
||||
|
||||
handle = ACPI_HANDLE(GET_HCIDEV_DEV(data->hdev));
|
||||
if (!handle) {
|
||||
bt_dev_err(data->hdev, "No support for bluetooth device in ACPI firmware");
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
if (!acpi_has_method(handle, "_PRR")) {
|
||||
bt_dev_err(data->hdev, "No support for _PRR ACPI method, cold boot");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
argv4.buffer.type = ACPI_TYPE_BUFFER;
|
||||
argv4.buffer.length = sizeof(mode);
|
||||
argv4.buffer.pointer = (void *)&mode;
|
||||
|
||||
obj = acpi_evaluate_dsm(handle, &btintel_guid_dsm, 0,
|
||||
BTINTEL_PCIE_DSM_DYNAMIC_PLDR, &argv4);
|
||||
if (!obj) {
|
||||
bt_dev_err(data->hdev, "Failed to call dsm to set reset method");
|
||||
return -EIO;
|
||||
}
|
||||
ACPI_FREE(obj);
|
||||
|
||||
pci_dev_lock(data->pdev);
|
||||
pci_save_state(data->pdev);
|
||||
ret = btintel_acpi_reset_method(hdev);
|
||||
if (ret)
|
||||
bt_dev_err(data->hdev, "ACPI _PRR reset failed (%d), PLDR incomplete",
|
||||
ret);
|
||||
pci_restore_state(data->pdev);
|
||||
pci_dev_unlock(data->pdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void btintel_pcie_perform_pldr(struct btintel_pcie_data *data)
|
||||
{
|
||||
struct pci_dev *pdev = data->pdev;
|
||||
struct pci_dev *wifi = NULL;
|
||||
struct pci_bus *bus;
|
||||
int ret;
|
||||
/* on integrated we have to look up by ID (same bus) */
|
||||
static const struct pci_device_id wifi_device_ids[] = {
|
||||
#define WIFI_DEV(_id) { PCI_DEVICE(PCI_VENDOR_ID_INTEL, _id) }
|
||||
WIFI_DEV(0xA840), /* LNL */
|
||||
WIFI_DEV(0xE440), /* PTL-P */
|
||||
WIFI_DEV(0xE340), /* PTL-H */
|
||||
WIFI_DEV(0xD340), /* NVL-H */
|
||||
WIFI_DEV(0x6E70), /* NVL-S */
|
||||
WIFI_DEV(0x4D40), /* WCL */
|
||||
{}
|
||||
};
|
||||
struct pci_dev *tmp = NULL;
|
||||
|
||||
bus = pdev->bus;
|
||||
if (!bus)
|
||||
return;
|
||||
|
||||
list_for_each_entry(tmp, &bus->devices, bus_list) {
|
||||
if (pci_match_id(wifi_device_ids, tmp)) {
|
||||
wifi = pci_dev_get(tmp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (wifi)
|
||||
device_release_driver(&wifi->dev);
|
||||
|
||||
/* Wi-Fi is fully unbound before the reset and fully reprobed after
|
||||
* the normal PCI probe path handles all state setup from scratch.
|
||||
* BT needs pci_save_state()/pci_restore_state() because the BT driver
|
||||
* is still partially attached when the _PRR runs (it hasn't been unbound yet).
|
||||
* The PCI device needs to remain minimally functional so that
|
||||
* device_reprobe(&pdev->dev) can work afterward
|
||||
*/
|
||||
ret = btintel_pcie_acpi_reset_method(data);
|
||||
|
||||
if (wifi) {
|
||||
if (device_reprobe(&wifi->dev))
|
||||
BT_ERR("WiFi reprobe failed for BDF:%s", pci_name(wifi));
|
||||
pci_dev_put(wifi);
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
if (device_reprobe(&pdev->dev))
|
||||
BT_ERR("BT reprobe failed for BDF:%s", pci_name(pdev));
|
||||
}
|
||||
}
|
||||
|
||||
static void btintel_pcie_reset_work(struct work_struct *wk)
|
||||
{
|
||||
struct btintel_pcie_data *data =
|
||||
container_of(wk, struct btintel_pcie_data, reset_work);
|
||||
struct pci_dev *pdev = data->pdev;
|
||||
int err;
|
||||
|
||||
pci_lock_rescan_remove();
|
||||
|
||||
if (!pdev->bus)
|
||||
goto error;
|
||||
goto out;
|
||||
|
||||
data = pci_get_drvdata(pdev);
|
||||
if (!data)
|
||||
goto out;
|
||||
|
||||
btintel_pcie_disable_interrupts(data);
|
||||
btintel_pcie_synchronize_irqs(data);
|
||||
|
||||
flush_work(&data->rx_work);
|
||||
/* Drain any in-flight coredump and block new ones across reset.
|
||||
* Safe from self-deadlock: coredump_work runs on a separate wq.
|
||||
*/
|
||||
disable_work_sync(&data->coredump_work);
|
||||
|
||||
bt_dev_dbg(data->hdev, "Release bluetooth interface");
|
||||
if (data->reset_type == BTINTEL_PCIE_IOSF_PRR_PLDR) {
|
||||
/* This function holds pci_lock_rescan_remove(), which acquires
|
||||
* pci_rescan_remove_lock. This mutex serializes against PCI device
|
||||
* addition/removal (hotplug), so no device can be added to or
|
||||
* removed from the bus list while this code runs.
|
||||
*
|
||||
* device_reprobe() inside btintel_pcie_perform_pldr() destroys
|
||||
* 'data' via .remove(); a fresh probe re-INIT_WORKs the
|
||||
* coredump_work with disable count 0, so we must not call
|
||||
* enable_work() on this path.
|
||||
*/
|
||||
btintel_pcie_perform_pldr(data);
|
||||
goto out;
|
||||
}
|
||||
btintel_pcie_release_hdev(data);
|
||||
|
||||
err = pci_reset_function(pdev);
|
||||
/* Use pci_try_reset_function() rather than pci_reset_function() to
|
||||
* avoid an ABBA deadlock against btintel_pcie_remove(): the PCI core
|
||||
* calls .remove() with device_lock held, and remove() then waits for
|
||||
* this work via cancel_work_sync(); pci_reset_function() would in
|
||||
* turn try to acquire the same device_lock, deadlocking both paths.
|
||||
*/
|
||||
err = pci_try_reset_function(pdev);
|
||||
if (err) {
|
||||
BT_ERR("Failed resetting the pcie device (%d)", err);
|
||||
goto error;
|
||||
goto out_enable;
|
||||
}
|
||||
|
||||
btintel_pcie_enable_interrupts(data);
|
||||
@@ -2302,7 +2656,7 @@ static void btintel_pcie_removal_work(struct work_struct *wk)
|
||||
if (err) {
|
||||
BT_ERR("Failed to enable bluetooth hardware after reset (%d)",
|
||||
err);
|
||||
goto error;
|
||||
goto out_enable;
|
||||
}
|
||||
|
||||
btintel_pcie_reset_ia(data);
|
||||
@@ -2312,17 +2666,22 @@ static void btintel_pcie_removal_work(struct work_struct *wk)
|
||||
err = btintel_pcie_setup_hdev(data);
|
||||
if (err) {
|
||||
BT_ERR("Failed registering hdev (%d)", err);
|
||||
goto error;
|
||||
goto out_enable;
|
||||
}
|
||||
error:
|
||||
|
||||
out_enable:
|
||||
/* Balance disable_work_sync() above on every exit. Leaving the
|
||||
* counter incremented on a failed reset would permanently disable
|
||||
* coredump_work even after a later successful reset.
|
||||
*/
|
||||
enable_work(&data->coredump_work);
|
||||
out:
|
||||
pci_dev_put(pdev);
|
||||
pci_unlock_rescan_remove();
|
||||
kfree(removal);
|
||||
}
|
||||
|
||||
static void btintel_pcie_reset(struct hci_dev *hdev)
|
||||
{
|
||||
struct btintel_pcie_removal *removal;
|
||||
struct btintel_pcie_data *data;
|
||||
|
||||
data = hci_get_drvdata(hdev);
|
||||
@@ -2333,14 +2692,8 @@ static void btintel_pcie_reset(struct hci_dev *hdev)
|
||||
if (test_and_set_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &data->flags))
|
||||
return;
|
||||
|
||||
removal = kzalloc_obj(*removal, GFP_ATOMIC);
|
||||
if (!removal)
|
||||
return;
|
||||
|
||||
removal->pdev = data->pdev;
|
||||
INIT_WORK(&removal->work, btintel_pcie_removal_work);
|
||||
pci_dev_get(removal->pdev);
|
||||
schedule_work(&removal->work);
|
||||
pci_dev_get(data->pdev);
|
||||
schedule_work(&data->reset_work);
|
||||
}
|
||||
|
||||
static void btintel_pcie_hw_error(struct hci_dev *hdev, u8 code)
|
||||
@@ -2350,15 +2703,19 @@ static void btintel_pcie_hw_error(struct hci_dev *hdev, u8 code)
|
||||
struct pci_dev *pdev = dev_data->pdev;
|
||||
time64_t retry_window;
|
||||
|
||||
if (code == 0x13) {
|
||||
bt_dev_err(hdev, "Encountered top exception");
|
||||
return;
|
||||
}
|
||||
btintel_pcie_dump_debug_registers(hdev);
|
||||
|
||||
data = btintel_pcie_get_recovery(pdev, &hdev->dev);
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
if (code == 0x13)
|
||||
dev_data->reset_type = BTINTEL_PCIE_IOSF_PRR_PLDR;
|
||||
else
|
||||
dev_data->reset_type = BTINTEL_PCIE_IOSF_PRR_FLR;
|
||||
|
||||
bt_dev_err(hdev, "Encountered exception err:0x%x triggering: %s", code,
|
||||
dev_data->reset_type == BTINTEL_PCIE_IOSF_PRR_PLDR ? "PLDR" : "FLR");
|
||||
retry_window = ktime_get_boottime_seconds() - data->last_error;
|
||||
|
||||
if (retry_window < BTINTEL_PCIE_RESET_WINDOW_SECS &&
|
||||
@@ -2451,7 +2808,6 @@ static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data)
|
||||
hdev->bus = HCI_PCI;
|
||||
hci_set_drvdata(hdev, data);
|
||||
|
||||
data->hdev = hdev;
|
||||
SET_HCIDEV_DEV(hdev, &data->pdev->dev);
|
||||
|
||||
hdev->manufacturer = 2;
|
||||
@@ -2470,15 +2826,17 @@ static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data)
|
||||
err = hci_register_dev(hdev);
|
||||
if (err < 0) {
|
||||
BT_ERR("Failed to register to hdev (%d)", err);
|
||||
goto exit_error;
|
||||
hci_free_dev(hdev);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Publish hdev only after successful registration; the coredump
|
||||
* worker bails on !data->hdev, so it never observes a half-set-up
|
||||
* device.
|
||||
*/
|
||||
data->hdev = hdev;
|
||||
data->dmp_hdr.driver_name = KBUILD_MODNAME;
|
||||
return 0;
|
||||
|
||||
exit_error:
|
||||
hci_free_dev(hdev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int btintel_pcie_probe(struct pci_dev *pdev,
|
||||
@@ -2509,12 +2867,23 @@ static int btintel_pcie_probe(struct pci_dev *pdev,
|
||||
if (!data->workqueue)
|
||||
return -ENOMEM;
|
||||
|
||||
data->coredump_workqueue = alloc_ordered_workqueue(KBUILD_MODNAME "_cd", 0);
|
||||
if (!data->coredump_workqueue) {
|
||||
destroy_workqueue(data->workqueue);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
skb_queue_head_init(&data->rx_skb_q);
|
||||
INIT_WORK(&data->rx_work, btintel_pcie_rx_work);
|
||||
INIT_WORK(&data->reset_work, btintel_pcie_reset_work);
|
||||
INIT_WORK(&data->coredump_work, btintel_pcie_coredump_worker);
|
||||
|
||||
data->boot_stage_cache = 0x00;
|
||||
data->img_resp_cache = 0x00;
|
||||
|
||||
/* FLR can be invoked by echoing to debugfs path, so explicitly
|
||||
* initialized
|
||||
*/
|
||||
data->reset_type = BTINTEL_PCIE_IOSF_PRR_FLR;
|
||||
err = btintel_pcie_config_pcie(pdev, data);
|
||||
if (err)
|
||||
goto exit_error;
|
||||
@@ -2550,6 +2919,8 @@ exit_error:
|
||||
/* reset device before exit */
|
||||
btintel_pcie_reset_bt(data);
|
||||
|
||||
destroy_workqueue(data->coredump_workqueue);
|
||||
|
||||
pci_clear_master(pdev);
|
||||
|
||||
pci_set_drvdata(pdev, NULL);
|
||||
@@ -2562,6 +2933,25 @@ static void btintel_pcie_remove(struct pci_dev *pdev)
|
||||
struct btintel_pcie_data *data;
|
||||
|
||||
data = pci_get_drvdata(pdev);
|
||||
if (!data) {
|
||||
BT_WARN("PCI driver data is NULL, aborting remove");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Permanently block coredump triggers and drain the worker before
|
||||
* tearing down. Must run before cancel_work_sync(&reset_work) so
|
||||
* the disable counter stays >= 1 even after reset_work()'s
|
||||
* balanced enable_work() (counter 2 -> 1, never reaching 0).
|
||||
*/
|
||||
disable_work_sync(&data->coredump_work);
|
||||
|
||||
/* Cancel pending reset work. Skip only when remove() is called from
|
||||
* within the reset work itself (PLDR device_reprobe path) to avoid
|
||||
* deadlock. current_work() returns the work_struct of the caller if
|
||||
* we are in a workqueue context.
|
||||
*/
|
||||
if (current_work() != &data->reset_work)
|
||||
disable_work_sync(&data->reset_work);
|
||||
|
||||
btintel_pcie_disable_interrupts(data);
|
||||
|
||||
@@ -2581,6 +2971,7 @@ static void btintel_pcie_remove(struct pci_dev *pdev)
|
||||
|
||||
btintel_pcie_release_hdev(data);
|
||||
|
||||
destroy_workqueue(data->coredump_workqueue);
|
||||
destroy_workqueue(data->workqueue);
|
||||
|
||||
btintel_pcie_free(data);
|
||||
@@ -2596,11 +2987,19 @@ static void btintel_pcie_coredump(struct device *dev)
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
struct btintel_pcie_data *data = pci_get_drvdata(pdev);
|
||||
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
if (test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags))
|
||||
return;
|
||||
|
||||
data->dmp_hdr.trigger_reason = BTINTEL_PCIE_TRIGGER_REASON_USER_TRIGGER;
|
||||
queue_work(data->workqueue, &data->rx_work);
|
||||
/* queue_work() returns false if the work is disabled (reset or
|
||||
* remove in progress); clear the in-progress bit so a later
|
||||
* trigger can succeed once the work is re-enabled.
|
||||
*/
|
||||
if (!queue_work(data->coredump_workqueue, &data->coredump_work))
|
||||
clear_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -2712,6 +3111,7 @@ static int btintel_pcie_resume(struct device *dev)
|
||||
if (data->pm_sx_event == PM_EVENT_FREEZE ||
|
||||
data->pm_sx_event == PM_EVENT_HIBERNATE) {
|
||||
set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags);
|
||||
data->reset_type = BTINTEL_PCIE_IOSF_PRR_FLR;
|
||||
btintel_pcie_reset(data->hdev);
|
||||
return 0;
|
||||
}
|
||||
@@ -2740,7 +3140,7 @@ static int btintel_pcie_resume(struct device *dev)
|
||||
&data->flags)) {
|
||||
data->dmp_hdr.trigger_reason =
|
||||
BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT;
|
||||
queue_work(data->workqueue, &data->rx_work);
|
||||
queue_work(data->coredump_workqueue, &data->coredump_work);
|
||||
}
|
||||
set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags);
|
||||
btintel_pcie_reset(data->hdev);
|
||||
|
||||
@@ -68,6 +68,10 @@
|
||||
#define BTINTEL_PCIE_DBGC_CUR_DBGBUFF_STATUS (BTINTEL_PCIE_DBGC_BASE_ADDR + 0x1C)
|
||||
#define BTINTEL_PCIE_DBGC_DBGBUFF_WRAP_ARND (BTINTEL_PCIE_DBGC_BASE_ADDR + 0x2C)
|
||||
|
||||
#define BTINTEL_PCIE_DBGC_BASE_ADDR_SCP (0xf0d5d500)
|
||||
#define BTINTEL_PCIE_DBGC_CUR_DBGBUFF_STATUS_SCP (BTINTEL_PCIE_DBGC_BASE_ADDR_SCP + 0x1C)
|
||||
#define BTINTEL_PCIE_DBGC_DBGBUFF_WRAP_ARND_SCP (BTINTEL_PCIE_DBGC_BASE_ADDR_SCP + 0x2C)
|
||||
|
||||
#define BTINTEL_PCIE_DBG_IDX_BIT_MASK 0x0F
|
||||
#define BTINTEL_PCIE_DBGC_DBG_BUF_IDX(data) (((data) >> 24) & BTINTEL_PCIE_DBG_IDX_BIT_MASK)
|
||||
#define BTINTEL_PCIE_DBG_OFFSET_BIT_MASK 0xFFFFFF
|
||||
@@ -98,6 +102,7 @@ enum msix_hw_int_causes {
|
||||
BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0 = BIT(0), /* cause 32 */
|
||||
BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP1 = BIT(1), /* cause 33 */
|
||||
BTINTEL_PCIE_MSIX_HW_INT_CAUSES_HWEXP = BIT(3), /* cause 35 */
|
||||
BTINTEL_PCIE_MSIX_HW_INT_CAUSES_FWTRIG = BIT(5), /* cause 37 */
|
||||
};
|
||||
|
||||
/* PCIe device states
|
||||
@@ -115,6 +120,7 @@ enum {
|
||||
BTINTEL_PCIE_CORE_HALTED,
|
||||
BTINTEL_PCIE_HWEXP_INPROGRESS,
|
||||
BTINTEL_PCIE_COREDUMP_INPROGRESS,
|
||||
BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS,
|
||||
BTINTEL_PCIE_RECOVERY_IN_PROGRESS,
|
||||
BTINTEL_PCIE_SETUP_DONE
|
||||
};
|
||||
@@ -130,7 +136,9 @@ enum btintel_pcie_tlv_type {
|
||||
BTINTEL_DUMP_TIME,
|
||||
BTINTEL_FW_BUILD,
|
||||
BTINTEL_VENDOR,
|
||||
BTINTEL_DRIVER
|
||||
BTINTEL_DRIVER,
|
||||
BTINTEL_EVENT_TYPE,
|
||||
BTINTEL_EVENT_ID
|
||||
};
|
||||
|
||||
/* causes for the MBOX interrupts */
|
||||
@@ -141,6 +149,11 @@ enum msix_mbox_int_causes {
|
||||
BTINTEL_PCIE_CSR_MBOX_STATUS_MBOX4 = BIT(3), /* cause MBOX4 */
|
||||
};
|
||||
|
||||
enum btintel_pcie_reset_type {
|
||||
BTINTEL_PCIE_IOSF_PRR_FLR = 0,
|
||||
BTINTEL_PCIE_IOSF_PRR_PLDR = 1,
|
||||
};
|
||||
|
||||
#define BTINTEL_PCIE_MSIX_NON_AUTO_CLEAR_CAUSE BIT(7)
|
||||
|
||||
/* Minimum and Maximum number of MSI-X Vector
|
||||
@@ -425,6 +438,8 @@ struct btintel_pcie_dump_header {
|
||||
u32 wrap_ctr;
|
||||
u16 trigger_reason;
|
||||
int state;
|
||||
u8 event_type;
|
||||
u16 event_id;
|
||||
};
|
||||
|
||||
/* struct btintel_pcie_data
|
||||
@@ -451,6 +466,8 @@ struct btintel_pcie_dump_header {
|
||||
* @workqueue: workqueue for RX work
|
||||
* @rx_skb_q: SKB queue for RX packet
|
||||
* @rx_work: RX work struct to process the RX packet in @rx_skb_q
|
||||
* @coredump_workqueue: dedicated workqueue for coredump collection
|
||||
* @coredump_work: work struct for coredump trace collection
|
||||
* @dma_pool: DMA pool for descriptors, index array and ci
|
||||
* @dma_p_addr: DMA address for pool
|
||||
* @dma_v_addr: address of pool
|
||||
@@ -497,6 +514,10 @@ struct btintel_pcie_data {
|
||||
struct workqueue_struct *workqueue;
|
||||
struct sk_buff_head rx_skb_q;
|
||||
struct work_struct rx_work;
|
||||
struct work_struct reset_work;
|
||||
|
||||
struct workqueue_struct *coredump_workqueue;
|
||||
struct work_struct coredump_work;
|
||||
|
||||
struct dma_pool *dma_pool;
|
||||
dma_addr_t dma_p_addr;
|
||||
@@ -508,9 +529,12 @@ struct btintel_pcie_data {
|
||||
struct txq txq;
|
||||
struct rxq rxq;
|
||||
u32 alive_intr_ctxt;
|
||||
enum btintel_pcie_reset_type reset_type;
|
||||
struct btintel_pcie_dbgc dbgc;
|
||||
struct btintel_pcie_dump_header dmp_hdr;
|
||||
u8 pm_sx_event;
|
||||
u32 debug_evt_addr;
|
||||
u32 debug_evt_size;
|
||||
};
|
||||
|
||||
static inline u32 btintel_pcie_rd_reg32(struct btintel_pcie_data *data,
|
||||
|
||||
@@ -188,7 +188,7 @@ int btmtk_setup_firmware_79xx(struct hci_dev *hdev, const char *fwname,
|
||||
MTK_FW_ROM_PATCH_GD_SIZE +
|
||||
MTK_FW_ROM_PATCH_SEC_MAP_SIZE * i +
|
||||
MTK_SEC_MAP_COMMON_SIZE,
|
||||
MTK_SEC_MAP_NEED_SEND_SIZE + 1);
|
||||
MTK_SEC_MAP_NEED_SEND_SIZE);
|
||||
|
||||
wmt_params.op = BTMTK_WMT_PATCH_DWNLD;
|
||||
wmt_params.status = &status;
|
||||
@@ -1074,8 +1074,10 @@ struct urb *alloc_mtk_intr_urb(struct hci_dev *hdev, struct sk_buff *skb,
|
||||
if (!urb)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
if (btmtk_isopkt_pad(hdev, skb))
|
||||
if (btmtk_isopkt_pad(hdev, skb)) {
|
||||
usb_free_urb(urb);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
pipe = usb_sndintpipe(btmtk_data->udev,
|
||||
btmtk_data->isopkt_tx_ep->bEndpointAddress);
|
||||
@@ -1379,6 +1381,16 @@ int btmtk_usb_setup(struct hci_dev *hdev)
|
||||
break;
|
||||
case 0x7922:
|
||||
case 0x7925:
|
||||
/*
|
||||
* A remote wakeup could cause the device completely unresponsive, and
|
||||
* recovering from such a state needs a power cycle.
|
||||
*
|
||||
* Since the remote wakeup capability is super broken, just disable it
|
||||
* to get rid of the troubles. The device can still be autosuspended
|
||||
* when the bluetooth interface is closed.
|
||||
*/
|
||||
device_set_wakeup_capable(&btmtk_data->udev->dev, false);
|
||||
fallthrough;
|
||||
case 0x7961:
|
||||
case 0x7902:
|
||||
case 0x6639:
|
||||
@@ -1547,6 +1559,29 @@ int btmtk_usb_shutdown(struct hci_dev *hdev)
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(btmtk_usb_shutdown);
|
||||
|
||||
int btmtk_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_event_hdr *hdr = (void *)skb->data;
|
||||
struct hci_ev_cmd_complete *ec;
|
||||
|
||||
if (hdr->evt == HCI_EV_CMD_COMPLETE &&
|
||||
skb->len >= HCI_EVENT_HDR_SIZE + sizeof(*ec)) {
|
||||
u16 opcode;
|
||||
|
||||
ec = (void *)(skb->data + HCI_EVENT_HDR_SIZE);
|
||||
opcode = __le16_to_cpu(ec->opcode);
|
||||
|
||||
/* Filter vendor opcode */
|
||||
if (opcode == 0xfc5d) {
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return hci_recv_frame(hdev, skb);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(btmtk_recv_event);
|
||||
#endif
|
||||
|
||||
MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");
|
||||
|
||||
@@ -220,6 +220,8 @@ int btmtk_usb_suspend(struct hci_dev *hdev);
|
||||
int btmtk_usb_setup(struct hci_dev *hdev);
|
||||
|
||||
int btmtk_usb_shutdown(struct hci_dev *hdev);
|
||||
|
||||
int btmtk_recv_event(struct hci_dev *hdev, struct sk_buff *skb);
|
||||
#else
|
||||
|
||||
static inline int btmtk_set_bdaddr(struct hci_dev *hdev,
|
||||
@@ -299,4 +301,9 @@ static inline int btmtk_usb_shutdown(struct hci_dev *hdev)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int btmtk_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
return hci_recv_frame(hdev, skb);
|
||||
}
|
||||
#endif
|
||||
@@ -620,7 +620,7 @@ static void btmtksdio_txrx_work(struct work_struct *work)
|
||||
if (btmtksdio_rx_packet(bdev, rx_size) < 0)
|
||||
bdev->hdev->stat.err_rx++;
|
||||
}
|
||||
} while (int_status || time_is_before_jiffies(txrx_timeout));
|
||||
} while (int_status && time_is_after_jiffies(txrx_timeout));
|
||||
|
||||
/* Enable interrupt */
|
||||
if (bdev->func->irq_handler)
|
||||
|
||||
@@ -143,6 +143,8 @@ static int qca_read_fw_build_info(struct hci_dev *hdev)
|
||||
|
||||
hci_set_fw_info(hdev, "%s", build_label);
|
||||
|
||||
bt_dev_info(hdev, "QCA FW build version: %s", build_label);
|
||||
|
||||
kfree(build_label);
|
||||
out:
|
||||
kfree_skb(skb);
|
||||
|
||||
@@ -1,17 +1,7 @@
|
||||
// SPDX-License-Identifier: ISC
|
||||
/*
|
||||
* Copyright (c) 2017 Redpine Signals Inc.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, 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.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
@@ -1342,6 +1342,19 @@ void btrtl_set_quirks(struct hci_dev *hdev, struct btrtl_device_info *btrtl_dev)
|
||||
if (!btrtl_dev->ic_info)
|
||||
return;
|
||||
|
||||
switch (btrtl_dev->project_id) {
|
||||
case CHIP_ID_8761B:
|
||||
/* RTL8761B/BU reports HCI version 5.1 but does not support
|
||||
* the LE Extended Scan commands (Opcode 0x2042), causing
|
||||
* repeated -EBUSY failures when BlueZ attempts extended
|
||||
* scanning while a connection is active.
|
||||
*/
|
||||
hci_set_quirk(hdev, HCI_QUIRK_BROKEN_EXT_SCAN);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (btrtl_dev->ic_info->lmp_subver) {
|
||||
case RTL_ROM_LMP_8703B:
|
||||
/* 8723CS reports two pages for local ext features,
|
||||
|
||||
+74
-20
@@ -484,6 +484,7 @@ static const struct usb_device_id quirks_table[] = {
|
||||
{ USB_DEVICE(0x8087, 0x0037), .driver_info = BTUSB_INTEL_COMBINED },
|
||||
{ USB_DEVICE(0x8087, 0x0038), .driver_info = BTUSB_INTEL_COMBINED },
|
||||
{ USB_DEVICE(0x8087, 0x0039), .driver_info = BTUSB_INTEL_COMBINED },
|
||||
{ USB_DEVICE(0x8087, 0x0040), .driver_info = BTUSB_INTEL_COMBINED }, /* Lizard Peak 2 */
|
||||
{ USB_DEVICE(0x8087, 0x07da), .driver_info = BTUSB_CSR },
|
||||
{ USB_DEVICE(0x8087, 0x07dc), .driver_info = BTUSB_INTEL_COMBINED |
|
||||
BTUSB_INTEL_NO_WBS_SUPPORT |
|
||||
@@ -531,6 +532,8 @@ static const struct usb_device_id quirks_table[] = {
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x7392, 0xe611), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x2c4e, 0x0128), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
|
||||
/* Realtek 8852AE Bluetooth devices */
|
||||
{ USB_DEVICE(0x0bda, 0x2852), .driver_info = BTUSB_REALTEK |
|
||||
@@ -601,6 +604,10 @@ static const struct usb_device_id quirks_table[] = {
|
||||
/* Realtek 8922AE Bluetooth devices */
|
||||
{ USB_DEVICE(0x0bda, 0x8922), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x0bda, 0xd922), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x0bda, 0xd923), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x13d3, 0x3617), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x13d3, 0x3616), .driver_info = BTUSB_REALTEK |
|
||||
@@ -723,6 +730,8 @@ static const struct usb_device_id quirks_table[] = {
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x04ca, 0x38e4), .driver_info = BTUSB_MEDIATEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x0e8d, 0x223c), .driver_info = BTUSB_MEDIATEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x13d3, 0x3568), .driver_info = BTUSB_MEDIATEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x13d3, 0x3584), .driver_info = BTUSB_MEDIATEK |
|
||||
@@ -773,6 +782,8 @@ static const struct usb_device_id quirks_table[] = {
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x0489, 0xe151), .driver_info = BTUSB_MEDIATEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x0e8d, 0x8c38), .driver_info = BTUSB_MEDIATEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x13d3, 0x3602), .driver_info = BTUSB_MEDIATEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x13d3, 0x3603), .driver_info = BTUSB_MEDIATEK |
|
||||
@@ -781,6 +792,8 @@ static const struct usb_device_id quirks_table[] = {
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x13d3, 0x3608), .driver_info = BTUSB_MEDIATEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x13d3, 0x3609), .driver_info = BTUSB_MEDIATEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x13d3, 0x3613), .driver_info = BTUSB_MEDIATEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x13d3, 0x3627), .driver_info = BTUSB_MEDIATEK |
|
||||
@@ -814,8 +827,12 @@ static const struct usb_device_id quirks_table[] = {
|
||||
{ USB_DEVICE(0x2ff8, 0xb011), .driver_info = BTUSB_REALTEK },
|
||||
|
||||
/* Additional Realtek 8761BUV Bluetooth devices */
|
||||
{ USB_DEVICE(0x2c4e, 0x0115), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x2357, 0x0604), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x2357, 0x0607), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x0b05, 0x190e), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x2550, 0x8761), .driver_info = BTUSB_REALTEK |
|
||||
@@ -830,6 +847,8 @@ static const struct usb_device_id quirks_table[] = {
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x2b89, 0x6275), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x37ad, 0x0600), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
|
||||
/* Additional Realtek 8821AE Bluetooth devices */
|
||||
{ USB_DEVICE(0x0b05, 0x17dc), .driver_info = BTUSB_REALTEK },
|
||||
@@ -2994,6 +3013,11 @@ static int marvell_config_oob_wake(struct hci_dev *hdev)
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static inline int marvell_config_oob_wake(struct hci_dev *hdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int btusb_set_bdaddr_marvell(struct hci_dev *hdev,
|
||||
@@ -3819,8 +3843,7 @@ static int btusb_config_oob_wake(struct hci_dev *hdev)
|
||||
}
|
||||
|
||||
irq_set_status_flags(irq, IRQ_NOAUTOEN);
|
||||
ret = devm_request_irq(&hdev->dev, irq, btusb_oob_wake_handler,
|
||||
0, "OOB Wake-on-BT", data);
|
||||
ret = request_irq(irq, btusb_oob_wake_handler, 0, "OOB Wake-on-BT", data);
|
||||
if (ret) {
|
||||
bt_dev_err(hdev, "%s: IRQ request failed", __func__);
|
||||
return ret;
|
||||
@@ -3829,11 +3852,22 @@ static int btusb_config_oob_wake(struct hci_dev *hdev)
|
||||
ret = device_init_wakeup(dev, true);
|
||||
if (ret) {
|
||||
bt_dev_err(hdev, "%s: failed to init_wakeup", __func__);
|
||||
return ret;
|
||||
goto err_free_irq;
|
||||
}
|
||||
|
||||
data->oob_wake_irq = irq;
|
||||
bt_dev_info(hdev, "OOB Wake-on-BT configured at IRQ %u", irq);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
free_irq(irq, data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
static inline int btusb_config_oob_wake(struct hci_dev *hdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@@ -4085,10 +4119,8 @@ static int btusb_probe(struct usb_interface *intf,
|
||||
|
||||
err = usb_find_common_endpoints(intf->cur_altsetting, &data->bulk_rx_ep,
|
||||
&data->bulk_tx_ep, &data->intr_ep, NULL);
|
||||
if (err) {
|
||||
kfree(data);
|
||||
return -ENODEV;
|
||||
}
|
||||
if (err)
|
||||
goto err_free_data;
|
||||
|
||||
if (id->driver_info & BTUSB_AMP) {
|
||||
data->cmdreq_type = USB_TYPE_CLASS | 0x01;
|
||||
@@ -4144,8 +4176,8 @@ static int btusb_probe(struct usb_interface *intf,
|
||||
|
||||
hdev = hci_alloc_dev_priv(priv_size);
|
||||
if (!hdev) {
|
||||
kfree(data);
|
||||
return -ENOMEM;
|
||||
err = -ENOMEM;
|
||||
goto err_free_data;
|
||||
}
|
||||
|
||||
hdev->bus = HCI_USB;
|
||||
@@ -4159,7 +4191,7 @@ static int btusb_probe(struct usb_interface *intf,
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR(reset_gpio)) {
|
||||
err = PTR_ERR(reset_gpio);
|
||||
goto out_free_dev;
|
||||
goto err_free_hdev;
|
||||
} else if (reset_gpio) {
|
||||
data->reset_gpio = reset_gpio;
|
||||
}
|
||||
@@ -4172,18 +4204,17 @@ static int btusb_probe(struct usb_interface *intf,
|
||||
hdev->wakeup = btusb_wakeup;
|
||||
hdev->hci_drv = &btusb_hci_drv;
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
err = btusb_config_oob_wake(hdev);
|
||||
if (err)
|
||||
goto out_free_dev;
|
||||
goto err_put_reset;
|
||||
|
||||
/* Marvell devices may need a specific chip configuration */
|
||||
if (id->driver_info & BTUSB_MARVELL && data->oob_wake_irq) {
|
||||
err = marvell_config_oob_wake(hdev);
|
||||
if (err)
|
||||
goto out_free_dev;
|
||||
goto err_disable_wakeup;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (id->driver_info & BTUSB_CW6622)
|
||||
hci_set_quirk(hdev, HCI_QUIRK_BROKEN_STORED_LINK_KEY);
|
||||
|
||||
@@ -4218,7 +4249,7 @@ static int btusb_probe(struct usb_interface *intf,
|
||||
if (id->driver_info & BTUSB_INTEL_COMBINED) {
|
||||
err = btintel_configure_setup(hdev, btusb_driver.name);
|
||||
if (err)
|
||||
goto out_free_dev;
|
||||
goto err_kill_tx_urbs;
|
||||
|
||||
/* Transport specific configuration */
|
||||
hdev->send = btusb_send_frame_intel;
|
||||
@@ -4248,6 +4279,7 @@ static int btusb_probe(struct usb_interface *intf,
|
||||
hci_set_quirk(hdev, HCI_QUIRK_BROKEN_ENHANCED_SETUP_SYNC_CONN);
|
||||
hci_set_quirk(hdev, HCI_QUIRK_NON_PERSISTENT_SETUP);
|
||||
data->recv_acl = btmtk_usb_recv_acl;
|
||||
data->recv_event = btmtk_recv_event;
|
||||
data->suspend = btmtk_usb_suspend;
|
||||
data->resume = btmtk_usb_resume;
|
||||
data->disconnect = btusb_mtk_disconnect;
|
||||
@@ -4381,7 +4413,7 @@ static int btusb_probe(struct usb_interface *intf,
|
||||
err = usb_set_interface(data->udev, 0, 0);
|
||||
if (err < 0) {
|
||||
BT_ERR("failed to set interface 0, alt 0 %d", err);
|
||||
goto out_free_dev;
|
||||
goto err_kill_tx_urbs;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4389,7 +4421,7 @@ static int btusb_probe(struct usb_interface *intf,
|
||||
err = usb_driver_claim_interface(&btusb_driver,
|
||||
data->isoc, data);
|
||||
if (err < 0)
|
||||
goto out_free_dev;
|
||||
goto err_kill_tx_urbs;
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_BT_HCIBTUSB_BCM) && data->diag) {
|
||||
@@ -4407,7 +4439,7 @@ static int btusb_probe(struct usb_interface *intf,
|
||||
|
||||
err = hci_register_dev(hdev);
|
||||
if (err < 0)
|
||||
goto out_free_dev;
|
||||
goto err_release_siblings;
|
||||
|
||||
usb_set_intfdata(intf, data);
|
||||
|
||||
@@ -4416,11 +4448,30 @@ static int btusb_probe(struct usb_interface *intf,
|
||||
|
||||
return 0;
|
||||
|
||||
out_free_dev:
|
||||
err_release_siblings:
|
||||
if (data->diag) {
|
||||
usb_set_intfdata(data->diag, NULL);
|
||||
usb_driver_release_interface(&btusb_driver, data->diag);
|
||||
}
|
||||
if (data->isoc) {
|
||||
usb_set_intfdata(data->isoc, NULL);
|
||||
usb_driver_release_interface(&btusb_driver, data->isoc);
|
||||
}
|
||||
err_kill_tx_urbs:
|
||||
usb_kill_anchored_urbs(&data->tx_anchor);
|
||||
err_disable_wakeup:
|
||||
if (data->oob_wake_irq) {
|
||||
device_init_wakeup(&data->udev->dev, false);
|
||||
free_irq(data->oob_wake_irq, data);
|
||||
}
|
||||
err_put_reset:
|
||||
if (data->reset_gpio)
|
||||
gpiod_put(data->reset_gpio);
|
||||
err_free_hdev:
|
||||
hci_free_dev(hdev);
|
||||
err_free_data:
|
||||
kfree(data);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -4448,8 +4499,11 @@ static void btusb_disconnect(struct usb_interface *intf)
|
||||
|
||||
hci_unregister_dev(hdev);
|
||||
|
||||
if (data->oob_wake_irq)
|
||||
if (data->oob_wake_irq) {
|
||||
device_init_wakeup(&data->udev->dev, false);
|
||||
free_irq(data->oob_wake_irq, data);
|
||||
}
|
||||
|
||||
if (data->reset_gpio)
|
||||
gpiod_put(data->reset_gpio);
|
||||
|
||||
|
||||
@@ -1,614 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* A driver for Nokia Connectivity Card DTL-1 devices
|
||||
*
|
||||
* Copyright (C) 2001-2002 Marcel Holtmann <marcel@holtmann.org>
|
||||
*
|
||||
*
|
||||
* 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;
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS
|
||||
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
* implied. See the License for the specific language governing
|
||||
* rights and limitations under the License.
|
||||
*
|
||||
* The initial developer of the original code is David A. Hinds
|
||||
* <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
|
||||
* are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/moduleparam.h>
|
||||
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/serial.h>
|
||||
#include <linux/serial_reg.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include <pcmcia/cistpl.h>
|
||||
#include <pcmcia/ciscode.h>
|
||||
#include <pcmcia/ds.h>
|
||||
#include <pcmcia/cisreg.h>
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
|
||||
|
||||
|
||||
/* ======================== Module parameters ======================== */
|
||||
|
||||
|
||||
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
|
||||
MODULE_DESCRIPTION("Bluetooth driver for Nokia Connectivity Card DTL-1");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
|
||||
|
||||
/* ======================== Local structures ======================== */
|
||||
|
||||
|
||||
struct dtl1_info {
|
||||
struct pcmcia_device *p_dev;
|
||||
|
||||
struct hci_dev *hdev;
|
||||
|
||||
spinlock_t lock; /* For serializing operations */
|
||||
|
||||
unsigned long flowmask; /* HCI flow mask */
|
||||
int ri_latch;
|
||||
|
||||
struct sk_buff_head txq;
|
||||
unsigned long tx_state;
|
||||
|
||||
unsigned long rx_state;
|
||||
unsigned long rx_count;
|
||||
struct sk_buff *rx_skb;
|
||||
};
|
||||
|
||||
|
||||
static int dtl1_config(struct pcmcia_device *link);
|
||||
|
||||
|
||||
/* Transmit states */
|
||||
#define XMIT_SENDING 1
|
||||
#define XMIT_WAKEUP 2
|
||||
#define XMIT_WAITING 8
|
||||
|
||||
/* Receiver States */
|
||||
#define RECV_WAIT_NSH 0
|
||||
#define RECV_WAIT_DATA 1
|
||||
|
||||
|
||||
struct nsh {
|
||||
u8 type;
|
||||
u8 zero;
|
||||
u16 len;
|
||||
} __packed; /* Nokia Specific Header */
|
||||
|
||||
#define NSHL 4 /* Nokia Specific Header Length */
|
||||
|
||||
|
||||
|
||||
/* ======================== Interrupt handling ======================== */
|
||||
|
||||
|
||||
static int dtl1_write(unsigned int iobase, int fifo_size, __u8 *buf, int len)
|
||||
{
|
||||
int actual = 0;
|
||||
|
||||
/* Tx FIFO should be empty */
|
||||
if (!(inb(iobase + UART_LSR) & UART_LSR_THRE))
|
||||
return 0;
|
||||
|
||||
/* Fill FIFO with current frame */
|
||||
while ((fifo_size-- > 0) && (actual < len)) {
|
||||
/* Transmit next byte */
|
||||
outb(buf[actual], iobase + UART_TX);
|
||||
actual++;
|
||||
}
|
||||
|
||||
return actual;
|
||||
}
|
||||
|
||||
|
||||
static void dtl1_write_wakeup(struct dtl1_info *info)
|
||||
{
|
||||
if (!info) {
|
||||
BT_ERR("Unknown device");
|
||||
return;
|
||||
}
|
||||
|
||||
if (test_bit(XMIT_WAITING, &(info->tx_state))) {
|
||||
set_bit(XMIT_WAKEUP, &(info->tx_state));
|
||||
return;
|
||||
}
|
||||
|
||||
if (test_and_set_bit(XMIT_SENDING, &(info->tx_state))) {
|
||||
set_bit(XMIT_WAKEUP, &(info->tx_state));
|
||||
return;
|
||||
}
|
||||
|
||||
do {
|
||||
unsigned int iobase = info->p_dev->resource[0]->start;
|
||||
register struct sk_buff *skb;
|
||||
int len;
|
||||
|
||||
clear_bit(XMIT_WAKEUP, &(info->tx_state));
|
||||
|
||||
if (!pcmcia_dev_present(info->p_dev))
|
||||
return;
|
||||
|
||||
skb = skb_dequeue(&(info->txq));
|
||||
if (!skb)
|
||||
break;
|
||||
|
||||
/* Send frame */
|
||||
len = dtl1_write(iobase, 32, skb->data, skb->len);
|
||||
|
||||
if (len == skb->len) {
|
||||
set_bit(XMIT_WAITING, &(info->tx_state));
|
||||
kfree_skb(skb);
|
||||
} else {
|
||||
skb_pull(skb, len);
|
||||
skb_queue_head(&(info->txq), skb);
|
||||
}
|
||||
|
||||
info->hdev->stat.byte_tx += len;
|
||||
|
||||
} while (test_bit(XMIT_WAKEUP, &(info->tx_state)));
|
||||
|
||||
clear_bit(XMIT_SENDING, &(info->tx_state));
|
||||
}
|
||||
|
||||
|
||||
static void dtl1_control(struct dtl1_info *info, struct sk_buff *skb)
|
||||
{
|
||||
u8 flowmask = *(u8 *)skb->data;
|
||||
int i;
|
||||
|
||||
printk(KERN_INFO "Bluetooth: Nokia control data =");
|
||||
for (i = 0; i < skb->len; i++)
|
||||
printk(" %02x", skb->data[i]);
|
||||
|
||||
printk("\n");
|
||||
|
||||
/* transition to active state */
|
||||
if (((info->flowmask & 0x07) == 0) && ((flowmask & 0x07) != 0)) {
|
||||
clear_bit(XMIT_WAITING, &(info->tx_state));
|
||||
dtl1_write_wakeup(info);
|
||||
}
|
||||
|
||||
info->flowmask = flowmask;
|
||||
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
|
||||
static void dtl1_receive(struct dtl1_info *info)
|
||||
{
|
||||
unsigned int iobase;
|
||||
struct nsh *nsh;
|
||||
int boguscount = 0;
|
||||
|
||||
if (!info) {
|
||||
BT_ERR("Unknown device");
|
||||
return;
|
||||
}
|
||||
|
||||
iobase = info->p_dev->resource[0]->start;
|
||||
|
||||
do {
|
||||
info->hdev->stat.byte_rx++;
|
||||
|
||||
/* Allocate packet */
|
||||
if (info->rx_skb == NULL) {
|
||||
info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
|
||||
if (!info->rx_skb) {
|
||||
BT_ERR("Can't allocate mem for new packet");
|
||||
info->rx_state = RECV_WAIT_NSH;
|
||||
info->rx_count = NSHL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
skb_put_u8(info->rx_skb, inb(iobase + UART_RX));
|
||||
nsh = (struct nsh *)info->rx_skb->data;
|
||||
|
||||
info->rx_count--;
|
||||
|
||||
if (info->rx_count == 0) {
|
||||
|
||||
switch (info->rx_state) {
|
||||
case RECV_WAIT_NSH:
|
||||
info->rx_state = RECV_WAIT_DATA;
|
||||
info->rx_count = nsh->len + (nsh->len & 0x0001);
|
||||
break;
|
||||
case RECV_WAIT_DATA:
|
||||
hci_skb_pkt_type(info->rx_skb) = nsh->type;
|
||||
|
||||
/* remove PAD byte if it exists */
|
||||
if (nsh->len & 0x0001) {
|
||||
info->rx_skb->tail--;
|
||||
info->rx_skb->len--;
|
||||
}
|
||||
|
||||
/* remove NSH */
|
||||
skb_pull(info->rx_skb, NSHL);
|
||||
|
||||
switch (hci_skb_pkt_type(info->rx_skb)) {
|
||||
case 0x80:
|
||||
/* control data for the Nokia Card */
|
||||
dtl1_control(info, info->rx_skb);
|
||||
break;
|
||||
case 0x82:
|
||||
case 0x83:
|
||||
case 0x84:
|
||||
/* send frame to the HCI layer */
|
||||
hci_skb_pkt_type(info->rx_skb) &= 0x0f;
|
||||
hci_recv_frame(info->hdev, info->rx_skb);
|
||||
break;
|
||||
default:
|
||||
/* unknown packet */
|
||||
BT_ERR("Unknown HCI packet with type 0x%02x received",
|
||||
hci_skb_pkt_type(info->rx_skb));
|
||||
kfree_skb(info->rx_skb);
|
||||
break;
|
||||
}
|
||||
|
||||
info->rx_state = RECV_WAIT_NSH;
|
||||
info->rx_count = NSHL;
|
||||
info->rx_skb = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Make sure we don't stay here too long */
|
||||
if (boguscount++ > 32)
|
||||
break;
|
||||
|
||||
} while (inb(iobase + UART_LSR) & UART_LSR_DR);
|
||||
}
|
||||
|
||||
|
||||
static irqreturn_t dtl1_interrupt(int irq, void *dev_inst)
|
||||
{
|
||||
struct dtl1_info *info = dev_inst;
|
||||
unsigned int iobase;
|
||||
unsigned char msr;
|
||||
int boguscount = 0;
|
||||
int iir, lsr;
|
||||
irqreturn_t r = IRQ_NONE;
|
||||
|
||||
if (!info || !info->hdev)
|
||||
/* our irq handler is shared */
|
||||
return IRQ_NONE;
|
||||
|
||||
iobase = info->p_dev->resource[0]->start;
|
||||
|
||||
spin_lock(&(info->lock));
|
||||
|
||||
iir = inb(iobase + UART_IIR) & UART_IIR_ID;
|
||||
while (iir) {
|
||||
|
||||
r = IRQ_HANDLED;
|
||||
/* Clear interrupt */
|
||||
lsr = inb(iobase + UART_LSR);
|
||||
|
||||
switch (iir) {
|
||||
case UART_IIR_RLSI:
|
||||
BT_ERR("RLSI");
|
||||
break;
|
||||
case UART_IIR_RDI:
|
||||
/* Receive interrupt */
|
||||
dtl1_receive(info);
|
||||
break;
|
||||
case UART_IIR_THRI:
|
||||
if (lsr & UART_LSR_THRE) {
|
||||
/* Transmitter ready for data */
|
||||
dtl1_write_wakeup(info);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
BT_ERR("Unhandled IIR=%#x", iir);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Make sure we don't stay here too long */
|
||||
if (boguscount++ > 100)
|
||||
break;
|
||||
|
||||
iir = inb(iobase + UART_IIR) & UART_IIR_ID;
|
||||
|
||||
}
|
||||
|
||||
msr = inb(iobase + UART_MSR);
|
||||
|
||||
if (info->ri_latch ^ (msr & UART_MSR_RI)) {
|
||||
info->ri_latch = msr & UART_MSR_RI;
|
||||
clear_bit(XMIT_WAITING, &(info->tx_state));
|
||||
dtl1_write_wakeup(info);
|
||||
r = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
spin_unlock(&(info->lock));
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ======================== HCI interface ======================== */
|
||||
|
||||
|
||||
static int dtl1_hci_open(struct hci_dev *hdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int dtl1_hci_flush(struct hci_dev *hdev)
|
||||
{
|
||||
struct dtl1_info *info = hci_get_drvdata(hdev);
|
||||
|
||||
/* Drop TX queue */
|
||||
skb_queue_purge(&(info->txq));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int dtl1_hci_close(struct hci_dev *hdev)
|
||||
{
|
||||
dtl1_hci_flush(hdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int dtl1_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct dtl1_info *info = hci_get_drvdata(hdev);
|
||||
struct sk_buff *s;
|
||||
struct nsh nsh;
|
||||
|
||||
switch (hci_skb_pkt_type(skb)) {
|
||||
case HCI_COMMAND_PKT:
|
||||
hdev->stat.cmd_tx++;
|
||||
nsh.type = 0x81;
|
||||
break;
|
||||
case HCI_ACLDATA_PKT:
|
||||
hdev->stat.acl_tx++;
|
||||
nsh.type = 0x82;
|
||||
break;
|
||||
case HCI_SCODATA_PKT:
|
||||
hdev->stat.sco_tx++;
|
||||
nsh.type = 0x83;
|
||||
break;
|
||||
default:
|
||||
return -EILSEQ;
|
||||
}
|
||||
|
||||
nsh.zero = 0;
|
||||
nsh.len = skb->len;
|
||||
|
||||
s = bt_skb_alloc(NSHL + skb->len + 1, GFP_ATOMIC);
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
|
||||
skb_reserve(s, NSHL);
|
||||
skb_copy_from_linear_data(skb, skb_put(s, skb->len), skb->len);
|
||||
if (skb->len & 0x0001)
|
||||
skb_put_u8(s, 0); /* PAD */
|
||||
|
||||
/* Prepend skb with Nokia frame header and queue */
|
||||
memcpy(skb_push(s, NSHL), &nsh, NSHL);
|
||||
skb_queue_tail(&(info->txq), s);
|
||||
|
||||
dtl1_write_wakeup(info);
|
||||
|
||||
kfree_skb(skb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ======================== Card services HCI interaction ======================== */
|
||||
|
||||
|
||||
static int dtl1_open(struct dtl1_info *info)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned int iobase = info->p_dev->resource[0]->start;
|
||||
struct hci_dev *hdev;
|
||||
|
||||
spin_lock_init(&(info->lock));
|
||||
|
||||
skb_queue_head_init(&(info->txq));
|
||||
|
||||
info->rx_state = RECV_WAIT_NSH;
|
||||
info->rx_count = NSHL;
|
||||
info->rx_skb = NULL;
|
||||
|
||||
set_bit(XMIT_WAITING, &(info->tx_state));
|
||||
|
||||
/* Initialize HCI device */
|
||||
hdev = hci_alloc_dev();
|
||||
if (!hdev) {
|
||||
BT_ERR("Can't allocate HCI device");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
info->hdev = hdev;
|
||||
|
||||
hdev->bus = HCI_PCCARD;
|
||||
hci_set_drvdata(hdev, info);
|
||||
SET_HCIDEV_DEV(hdev, &info->p_dev->dev);
|
||||
|
||||
hdev->open = dtl1_hci_open;
|
||||
hdev->close = dtl1_hci_close;
|
||||
hdev->flush = dtl1_hci_flush;
|
||||
hdev->send = dtl1_hci_send_frame;
|
||||
|
||||
spin_lock_irqsave(&(info->lock), flags);
|
||||
|
||||
/* Reset UART */
|
||||
outb(0, iobase + UART_MCR);
|
||||
|
||||
/* Turn off interrupts */
|
||||
outb(0, iobase + UART_IER);
|
||||
|
||||
/* Initialize UART */
|
||||
outb(UART_LCR_WLEN8, iobase + UART_LCR); /* Reset DLAB */
|
||||
outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), iobase + UART_MCR);
|
||||
|
||||
info->ri_latch = inb(info->p_dev->resource[0]->start + UART_MSR)
|
||||
& UART_MSR_RI;
|
||||
|
||||
/* Turn on interrupts */
|
||||
outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER);
|
||||
|
||||
spin_unlock_irqrestore(&(info->lock), flags);
|
||||
|
||||
/* Timeout before it is safe to send the first HCI packet */
|
||||
msleep(2000);
|
||||
|
||||
/* Register HCI device */
|
||||
if (hci_register_dev(hdev) < 0) {
|
||||
BT_ERR("Can't register HCI device");
|
||||
info->hdev = NULL;
|
||||
hci_free_dev(hdev);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int dtl1_close(struct dtl1_info *info)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned int iobase = info->p_dev->resource[0]->start;
|
||||
struct hci_dev *hdev = info->hdev;
|
||||
|
||||
if (!hdev)
|
||||
return -ENODEV;
|
||||
|
||||
dtl1_hci_close(hdev);
|
||||
|
||||
spin_lock_irqsave(&(info->lock), flags);
|
||||
|
||||
/* Reset UART */
|
||||
outb(0, iobase + UART_MCR);
|
||||
|
||||
/* Turn off interrupts */
|
||||
outb(0, iobase + UART_IER);
|
||||
|
||||
spin_unlock_irqrestore(&(info->lock), flags);
|
||||
|
||||
hci_unregister_dev(hdev);
|
||||
hci_free_dev(hdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dtl1_probe(struct pcmcia_device *link)
|
||||
{
|
||||
struct dtl1_info *info;
|
||||
|
||||
/* Create new info device */
|
||||
info = devm_kzalloc(&link->dev, sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
info->p_dev = link;
|
||||
link->priv = info;
|
||||
|
||||
link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
|
||||
|
||||
return dtl1_config(link);
|
||||
}
|
||||
|
||||
|
||||
static void dtl1_detach(struct pcmcia_device *link)
|
||||
{
|
||||
struct dtl1_info *info = link->priv;
|
||||
|
||||
dtl1_close(info);
|
||||
pcmcia_disable_device(link);
|
||||
}
|
||||
|
||||
static int dtl1_confcheck(struct pcmcia_device *p_dev, void *priv_data)
|
||||
{
|
||||
if ((p_dev->resource[1]->end) || (p_dev->resource[1]->end < 8))
|
||||
return -ENODEV;
|
||||
|
||||
p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
|
||||
p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
|
||||
|
||||
return pcmcia_request_io(p_dev);
|
||||
}
|
||||
|
||||
static int dtl1_config(struct pcmcia_device *link)
|
||||
{
|
||||
struct dtl1_info *info = link->priv;
|
||||
int ret;
|
||||
|
||||
/* Look for a generic full-sized window */
|
||||
link->resource[0]->end = 8;
|
||||
ret = pcmcia_loop_config(link, dtl1_confcheck, NULL);
|
||||
if (ret)
|
||||
goto failed;
|
||||
|
||||
ret = pcmcia_request_irq(link, dtl1_interrupt);
|
||||
if (ret)
|
||||
goto failed;
|
||||
|
||||
ret = pcmcia_enable_device(link);
|
||||
if (ret)
|
||||
goto failed;
|
||||
|
||||
ret = dtl1_open(info);
|
||||
if (ret)
|
||||
goto failed;
|
||||
|
||||
return 0;
|
||||
|
||||
failed:
|
||||
dtl1_detach(link);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct pcmcia_device_id dtl1_ids[] = {
|
||||
PCMCIA_DEVICE_PROD_ID12("Nokia Mobile Phones", "DTL-1", 0xe1bfdd64, 0xe168480d),
|
||||
PCMCIA_DEVICE_PROD_ID12("Nokia Mobile Phones", "DTL-4", 0xe1bfdd64, 0x9102bc82),
|
||||
PCMCIA_DEVICE_PROD_ID12("Socket", "CF", 0xb38bcc2e, 0x44ebf863),
|
||||
PCMCIA_DEVICE_PROD_ID12("Socket", "CF+ Personal Network Card", 0xb38bcc2e, 0xe732bae3),
|
||||
PCMCIA_DEVICE_NULL
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pcmcia, dtl1_ids);
|
||||
|
||||
static struct pcmcia_driver dtl1_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "dtl1_cs",
|
||||
.probe = dtl1_probe,
|
||||
.remove = dtl1_detach,
|
||||
.id_table = dtl1_ids,
|
||||
};
|
||||
module_pcmcia_driver(dtl1_driver);
|
||||
@@ -273,6 +273,7 @@ static int h5_close(struct hci_uart *hu)
|
||||
if (!hu->serdev)
|
||||
kfree(h5);
|
||||
|
||||
hu->priv = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -1028,7 +1028,7 @@ static void qca_dmp_hdr(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
skb_put_data(skb, buf, strlen(buf));
|
||||
|
||||
snprintf(buf, sizeof(buf), "Driver: %s\n",
|
||||
hu->serdev->dev.driver->name);
|
||||
hu->serdev ? hu->serdev->dev.driver->name : "hci_ldisc_qca");
|
||||
skb_put_data(skb, buf, strlen(buf));
|
||||
}
|
||||
|
||||
@@ -1916,9 +1916,12 @@ static int qca_setup(struct hci_uart *hu)
|
||||
const char *rampatch_name = qca_get_rampatch_name(hu);
|
||||
int ret;
|
||||
struct qca_btsoc_version ver;
|
||||
struct qca_serdev *qcadev = serdev_device_get_drvdata(hu->serdev);
|
||||
struct qca_serdev *qcadev = NULL;
|
||||
const char *soc_name;
|
||||
|
||||
if (hu->serdev)
|
||||
qcadev = serdev_device_get_drvdata(hu->serdev);
|
||||
|
||||
ret = qca_check_speeds(hu);
|
||||
if (ret)
|
||||
return ret;
|
||||
@@ -1980,7 +1983,7 @@ retry:
|
||||
case QCA_WCN6750:
|
||||
case QCA_WCN6855:
|
||||
case QCA_WCN7850:
|
||||
if (qcadev->bdaddr_property_broken)
|
||||
if (qcadev && qcadev->bdaddr_property_broken)
|
||||
hci_set_quirk(hdev, HCI_QUIRK_BDADDR_PROPERTY_BROKEN);
|
||||
|
||||
hci_set_aosp_capable(hdev);
|
||||
@@ -2073,7 +2076,7 @@ out:
|
||||
else
|
||||
hu->hdev->set_bdaddr = qca_set_bdaddr;
|
||||
|
||||
if (qcadev->support_hfp_hw_offload)
|
||||
if (qcadev && qcadev->support_hfp_hw_offload)
|
||||
qca_configure_hfp_offload(hdev);
|
||||
|
||||
qca->fw_version = le16_to_cpu(ver.patch_ver);
|
||||
|
||||
@@ -337,7 +337,17 @@ static ssize_t force_devcd_write(struct file *file, const char __user *user_buf,
|
||||
if (copy_from_user(&dump_data, user_buf, count))
|
||||
return -EFAULT;
|
||||
|
||||
switch (dump_data.state) {
|
||||
case HCI_DEVCOREDUMP_DONE:
|
||||
case HCI_DEVCOREDUMP_ABORT:
|
||||
case HCI_DEVCOREDUMP_TIMEOUT:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
data_size = count - offsetof(struct devcoredump_test_data, data);
|
||||
|
||||
skb = alloc_skb(data_size, GFP_ATOMIC);
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
BlueZ - Bluetooth protocol stack for Linux
|
||||
Copyright (C) 2000-2001 Qualcomm Incorporated
|
||||
@@ -5,10 +6,6 @@
|
||||
|
||||
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.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
BlueZ - Bluetooth protocol stack for Linux
|
||||
Copyright (C) 2000-2001 Qualcomm Incorporated
|
||||
@@ -5,10 +6,6 @@
|
||||
|
||||
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.
|
||||
@@ -2486,6 +2483,12 @@ struct hci_rp_le_cs_test {
|
||||
|
||||
#define HCI_OP_LE_CS_TEST_END 0x2096
|
||||
|
||||
#define HCI_OP_LE_SET_HOST_FEATURE_V2 0x2097
|
||||
struct hci_cp_le_set_host_feature_v2 {
|
||||
__le16 bit_number;
|
||||
__u8 bit_value;
|
||||
} __packed;
|
||||
|
||||
/* ---- HCI Events ---- */
|
||||
struct hci_ev_status {
|
||||
__u8 status;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
BlueZ - Bluetooth protocol stack for Linux
|
||||
Copyright (c) 2000-2001, 2010, Code Aurora Forum. All rights reserved.
|
||||
@@ -5,10 +6,6 @@
|
||||
|
||||
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.
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
BlueZ - Bluetooth protocol stack for Linux
|
||||
|
||||
Copyright (C) 2011-2012 Intel Corporation
|
||||
|
||||
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.
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
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.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
BlueZ - Bluetooth protocol stack for Linux
|
||||
Copyright (C) 2000-2001 Qualcomm Incorporated
|
||||
@@ -6,10 +7,6 @@
|
||||
|
||||
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.
|
||||
@@ -748,6 +745,7 @@ enum {
|
||||
FLAG_ECRED_CONN_REQ_SENT,
|
||||
FLAG_PENDING_SECURITY,
|
||||
FLAG_HOLD_HCI_CONN,
|
||||
FLAG_DEL,
|
||||
};
|
||||
|
||||
/* Lock nesting levels for L2CAP channels. We need these because lockdep
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
BlueZ - Bluetooth protocol stack for Linux
|
||||
|
||||
Copyright (C) 2010 Nokia Corporation
|
||||
Copyright (C) 2011-2012 Intel Corporation
|
||||
|
||||
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.
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
RFCOMM implementation for Linux Bluetooth stack (BlueZ)
|
||||
Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>
|
||||
Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org>
|
||||
|
||||
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.
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
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.
|
||||
|
||||
+23
-2
@@ -760,13 +760,33 @@ static inline struct l2cap_chan *chan_new_conn_cb(struct l2cap_chan *pchan)
|
||||
return chan;
|
||||
}
|
||||
|
||||
static void unregister_dev(struct lowpan_btle_dev *dev)
|
||||
{
|
||||
struct hci_dev *hdev = READ_ONCE(dev->hdev);
|
||||
|
||||
/* If netdev holds last reference to hci_dev (its parent device), this
|
||||
* leads to theoretical cyclic locking on lowpan_unregister_netdev:
|
||||
*
|
||||
* rtnl_lock -> put_device(parent) -> hci_release_dev ->
|
||||
* destroy_workqueue -> hci_rx_work -> l2cap_recv_acldata ->
|
||||
* chan_ready_cb -> ifup -> rtnl_lock
|
||||
*
|
||||
* However, hci_rx_work is disabled in hci_unregister_dev, so this
|
||||
* should not occur. Make lockdep happy by postponing hdev release after
|
||||
* netdev put.
|
||||
*/
|
||||
hci_dev_hold(hdev);
|
||||
lowpan_unregister_netdev(dev->netdev);
|
||||
hci_dev_put(hdev);
|
||||
}
|
||||
|
||||
static void delete_netdev(struct work_struct *work)
|
||||
{
|
||||
struct lowpan_btle_dev *entry = container_of(work,
|
||||
struct lowpan_btle_dev,
|
||||
delete_netdev);
|
||||
|
||||
lowpan_unregister_netdev(entry->netdev);
|
||||
unregister_dev(entry);
|
||||
|
||||
/* The entry pointer is deleted by the netdev destructor. */
|
||||
}
|
||||
@@ -1252,6 +1272,7 @@ static void disconnect_devices(void)
|
||||
break;
|
||||
|
||||
new_dev->netdev = entry->netdev;
|
||||
new_dev->hdev = entry->hdev;
|
||||
INIT_LIST_HEAD(&new_dev->list);
|
||||
|
||||
list_add_rcu(&new_dev->list, &devices);
|
||||
@@ -1263,7 +1284,7 @@ static void disconnect_devices(void)
|
||||
ifdown(entry->netdev);
|
||||
BT_DBG("Unregistering netdev %s %p",
|
||||
entry->netdev->name, entry->netdev);
|
||||
lowpan_unregister_netdev(entry->netdev);
|
||||
unregister_dev(entry);
|
||||
kfree(entry);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,12 +9,8 @@ menuconfig BT
|
||||
depends on RFKILL || !RFKILL
|
||||
select CRC16
|
||||
select CRYPTO
|
||||
select CRYPTO_SKCIPHER
|
||||
select CRYPTO_LIB_AES
|
||||
imply CRYPTO_AES
|
||||
select CRYPTO_CMAC
|
||||
select CRYPTO_ECB
|
||||
select CRYPTO_SHA256
|
||||
select CRYPTO_LIB_AES_CBC_MACS
|
||||
select CRYPTO_ECDH
|
||||
help
|
||||
Bluetooth is low-cost, low-power, short-range wireless technology.
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
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.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
BNEP implementation for Linux Bluetooth stack (BlueZ).
|
||||
Copyright (C) 2001-2002 Inventel Systemes
|
||||
@@ -7,10 +8,6 @@
|
||||
|
||||
Copyright (C) 2002 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.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
BNEP implementation for Linux Bluetooth stack (BlueZ).
|
||||
Copyright (C) 2001-2002 Inventel Systemes
|
||||
@@ -7,10 +8,6 @@
|
||||
|
||||
Copyright (C) 2002 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.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
BNEP implementation for Linux Bluetooth stack (BlueZ).
|
||||
Copyright (C) 2001-2002 Inventel Systemes
|
||||
@@ -6,10 +7,6 @@
|
||||
|
||||
Copyright (C) 2002 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.
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* ECDH helper functions - KPP wrappings
|
||||
*
|
||||
* Copyright (C) 2017 Intel Corporation
|
||||
*
|
||||
* 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.
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* ECDH helper functions - KPP wrappings
|
||||
*
|
||||
* Copyright (C) 2017 Intel Corporation
|
||||
*
|
||||
* 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.
|
||||
|
||||
+5
-3
@@ -283,10 +283,12 @@ u8 eir_create_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr, u8 size)
|
||||
if (!flags)
|
||||
flags |= mgmt_get_adv_discov_flags(hdev);
|
||||
|
||||
/* If flags would still be empty, then there is no need to
|
||||
* include the "Flags" AD field".
|
||||
/* Only add the "Flags" if it fits together with the instance
|
||||
* advertising data; drop it rather than overflow the buffer.
|
||||
*/
|
||||
if (flags && (ad_len + eir_precalc_len(1) <= size)) {
|
||||
if (flags &&
|
||||
(ad_len + eir_precalc_len(1) +
|
||||
(adv ? adv->adv_data_len : 0) <= size)) {
|
||||
ptr[0] = 0x02;
|
||||
ptr[1] = EIR_FLAGS;
|
||||
ptr[2] = flags;
|
||||
|
||||
@@ -100,7 +100,7 @@ static void hci_read_codec_capabilities(struct hci_dev *hdev, __u8 transport,
|
||||
caps = (void *)skb->data;
|
||||
if (skb->len < sizeof(*caps))
|
||||
goto error;
|
||||
if (skb->len < caps->len)
|
||||
if (skb->len < sizeof(caps->len) + caps->len)
|
||||
goto error;
|
||||
len += sizeof(caps->len) + caps->len;
|
||||
skb_pull(skb, sizeof(caps->len) + caps->len);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
BlueZ - Bluetooth protocol stack for Linux
|
||||
Copyright (c) 2000-2001, 2010, Code Aurora Forum. All rights reserved.
|
||||
@@ -5,10 +6,6 @@
|
||||
|
||||
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.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
BlueZ - Bluetooth protocol stack for Linux
|
||||
Copyright (C) 2000-2001 Qualcomm Incorporated
|
||||
@@ -5,10 +6,6 @@
|
||||
|
||||
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.
|
||||
@@ -2671,6 +2668,8 @@ void hci_unregister_dev(struct hci_dev *hdev)
|
||||
disable_work_sync(&hdev->tx_work);
|
||||
disable_work_sync(&hdev->power_on);
|
||||
disable_work_sync(&hdev->error_reset);
|
||||
disable_delayed_work_sync(&hdev->cmd_timer);
|
||||
disable_delayed_work_sync(&hdev->ncmd_timer);
|
||||
|
||||
hci_cmd_sync_clear(hdev);
|
||||
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
BlueZ - Bluetooth protocol stack for Linux
|
||||
|
||||
Copyright (C) 2014 Intel Corporation
|
||||
|
||||
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.
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
BlueZ - Bluetooth protocol stack for Linux
|
||||
Copyright (C) 2014 Intel Corporation
|
||||
|
||||
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.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
BlueZ - Bluetooth protocol stack for Linux
|
||||
Copyright (c) 2000-2001, 2010, Code Aurora Forum. All rights reserved.
|
||||
@@ -5,10 +6,6 @@
|
||||
|
||||
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.
|
||||
@@ -1769,6 +1766,13 @@ static void le_set_scan_enable_complete(struct hci_dev *hdev, u8 enable)
|
||||
|
||||
hci_dev_clear_flag(hdev, HCI_LE_SCAN);
|
||||
|
||||
if (hdev->discovery.type == DISCOV_TYPE_INTERLEAVED &&
|
||||
hci_test_quirk(hdev, HCI_QUIRK_SIMULTANEOUS_DISCOVERY) &&
|
||||
!test_bit(HCI_INQUIRY, &hdev->flags) &&
|
||||
hdev->discovery.state == DISCOVERY_FINDING) {
|
||||
hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
|
||||
}
|
||||
|
||||
/* The HCI_LE_SCAN_INTERRUPTED flag indicates that we
|
||||
* interrupted scanning due to a connect request. Mark
|
||||
* therefore discovery as stopped.
|
||||
|
||||
+16
-15
@@ -1,13 +1,10 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
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.
|
||||
@@ -27,6 +24,7 @@
|
||||
#include <linux/export.h>
|
||||
#include <linux/utsname.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/uio.h>
|
||||
#include <linux/unaligned.h>
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
@@ -2063,7 +2061,7 @@ done:
|
||||
}
|
||||
|
||||
static int hci_sock_getsockopt_old(struct socket *sock, int level, int optname,
|
||||
char __user *optval, int __user *optlen)
|
||||
sockopt_t *sopt)
|
||||
{
|
||||
struct hci_ufilter uf;
|
||||
struct sock *sk = sock->sk;
|
||||
@@ -2071,8 +2069,7 @@ static int hci_sock_getsockopt_old(struct socket *sock, int level, int optname,
|
||||
|
||||
BT_DBG("sk %p, opt %d", sk, optname);
|
||||
|
||||
if (get_user(len, optlen))
|
||||
return -EFAULT;
|
||||
len = sopt->optlen;
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
@@ -2088,7 +2085,8 @@ static int hci_sock_getsockopt_old(struct socket *sock, int level, int optname,
|
||||
else
|
||||
opt = 0;
|
||||
|
||||
if (put_user(opt, optval))
|
||||
if (copy_to_iter(&opt, sizeof(opt), &sopt->iter_out) !=
|
||||
sizeof(opt))
|
||||
err = -EFAULT;
|
||||
break;
|
||||
|
||||
@@ -2098,7 +2096,8 @@ static int hci_sock_getsockopt_old(struct socket *sock, int level, int optname,
|
||||
else
|
||||
opt = 0;
|
||||
|
||||
if (put_user(opt, optval))
|
||||
if (copy_to_iter(&opt, sizeof(opt), &sopt->iter_out) !=
|
||||
sizeof(opt))
|
||||
err = -EFAULT;
|
||||
break;
|
||||
|
||||
@@ -2114,7 +2113,7 @@ static int hci_sock_getsockopt_old(struct socket *sock, int level, int optname,
|
||||
}
|
||||
|
||||
len = min_t(unsigned int, len, sizeof(uf));
|
||||
if (copy_to_user(optval, &uf, len))
|
||||
if (copy_to_iter(&uf, len, &sopt->iter_out) != len)
|
||||
err = -EFAULT;
|
||||
break;
|
||||
|
||||
@@ -2129,16 +2128,16 @@ done:
|
||||
}
|
||||
|
||||
static int hci_sock_getsockopt(struct socket *sock, int level, int optname,
|
||||
char __user *optval, int __user *optlen)
|
||||
sockopt_t *sopt)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
int err = 0;
|
||||
u16 mtu;
|
||||
|
||||
BT_DBG("sk %p, opt %d", sk, optname);
|
||||
|
||||
if (level == SOL_HCI)
|
||||
return hci_sock_getsockopt_old(sock, level, optname, optval,
|
||||
optlen);
|
||||
return hci_sock_getsockopt_old(sock, level, optname, sopt);
|
||||
|
||||
if (level != SOL_BLUETOOTH)
|
||||
return -ENOPROTOOPT;
|
||||
@@ -2148,7 +2147,9 @@ static int hci_sock_getsockopt(struct socket *sock, int level, int optname,
|
||||
switch (optname) {
|
||||
case BT_SNDMTU:
|
||||
case BT_RCVMTU:
|
||||
if (put_user(hci_pi(sk)->mtu, (u16 __user *)optval))
|
||||
mtu = hci_pi(sk)->mtu;
|
||||
if (copy_to_iter(&mtu, sizeof(mtu), &sopt->iter_out) !=
|
||||
sizeof(mtu))
|
||||
err = -EFAULT;
|
||||
break;
|
||||
|
||||
@@ -2185,7 +2186,7 @@ static const struct proto_ops hci_sock_ops = {
|
||||
.listen = sock_no_listen,
|
||||
.shutdown = sock_no_shutdown,
|
||||
.setsockopt = hci_sock_setsockopt,
|
||||
.getsockopt = hci_sock_getsockopt,
|
||||
.getsockopt_iter = hci_sock_getsockopt,
|
||||
.connect = sock_no_connect,
|
||||
.socketpair = sock_no_socketpair,
|
||||
.accept = sock_no_accept,
|
||||
|
||||
@@ -4588,11 +4588,33 @@ static int hci_set_le_support_sync(struct hci_dev *hdev)
|
||||
sizeof(cp), &cp, HCI_CMD_TIMEOUT);
|
||||
}
|
||||
|
||||
/* LE Set Host Feature V2 */
|
||||
static int hci_le_set_host_feature_v2_sync(struct hci_dev *hdev, u16 bit,
|
||||
u8 value)
|
||||
{
|
||||
struct hci_cp_le_set_host_feature_v2 cp;
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
|
||||
/* Connected Isochronous Channels (Host Support) */
|
||||
cp.bit_number = cpu_to_le16(bit);
|
||||
cp.bit_value = value;
|
||||
|
||||
return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_HOST_FEATURE_V2,
|
||||
sizeof(cp), &cp, HCI_CMD_TIMEOUT);
|
||||
}
|
||||
|
||||
/* LE Set Host Feature */
|
||||
static int hci_le_set_host_feature_sync(struct hci_dev *hdev, u8 bit, u8 value)
|
||||
static int hci_le_set_host_feature_sync(struct hci_dev *hdev, u16 bit, u8 value)
|
||||
{
|
||||
struct hci_cp_le_set_host_feature cp;
|
||||
|
||||
if (ll_ext_feature_capable(hdev) && hdev->commands[47] & BIT(4))
|
||||
return hci_le_set_host_feature_v2_sync(hdev, bit, value);
|
||||
|
||||
if (bit > 255)
|
||||
return 0;
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
|
||||
/* Connected Isochronous Channels (Host Support) */
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
HIDP implementation for Linux Bluetooth stack (BlueZ).
|
||||
Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org>
|
||||
Copyright (C) 2013 David Herrmann <dh.herrmann@gmail.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.
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
HIDP implementation for Linux Bluetooth stack (BlueZ).
|
||||
Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org>
|
||||
|
||||
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.
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
HIDP implementation for Linux Bluetooth stack (BlueZ).
|
||||
Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org>
|
||||
|
||||
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.
|
||||
|
||||
+14
-13
@@ -10,6 +10,7 @@
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/sched/signal.h>
|
||||
#include <linux/uio.h>
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
@@ -1882,18 +1883,17 @@ static int iso_sock_setsockopt(struct socket *sock, int level, int optname,
|
||||
}
|
||||
|
||||
static int iso_sock_getsockopt(struct socket *sock, int level, int optname,
|
||||
char __user *optval, int __user *optlen)
|
||||
sockopt_t *opt)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
int len, err = 0;
|
||||
struct bt_iso_qos *qos;
|
||||
int len, val, err = 0;
|
||||
u8 base_len;
|
||||
u8 *base;
|
||||
|
||||
BT_DBG("sk %p", sk);
|
||||
|
||||
if (get_user(len, optlen))
|
||||
return -EFAULT;
|
||||
len = opt->optlen;
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
@@ -1904,15 +1904,17 @@ static int iso_sock_getsockopt(struct socket *sock, int level, int optname,
|
||||
break;
|
||||
}
|
||||
|
||||
if (put_user(test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags),
|
||||
(u32 __user *)optval))
|
||||
val = test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags);
|
||||
if (copy_to_iter(&val, sizeof(val), &opt->iter_out) !=
|
||||
sizeof(val))
|
||||
err = -EFAULT;
|
||||
|
||||
break;
|
||||
|
||||
case BT_PKT_STATUS:
|
||||
if (put_user(test_bit(BT_SK_PKT_STATUS, &bt_sk(sk)->flags),
|
||||
(int __user *)optval))
|
||||
val = test_bit(BT_SK_PKT_STATUS, &bt_sk(sk)->flags);
|
||||
if (copy_to_iter(&val, sizeof(val), &opt->iter_out) !=
|
||||
sizeof(val))
|
||||
err = -EFAULT;
|
||||
break;
|
||||
|
||||
@@ -1920,7 +1922,7 @@ static int iso_sock_getsockopt(struct socket *sock, int level, int optname,
|
||||
qos = iso_sock_get_qos(sk);
|
||||
|
||||
len = min_t(unsigned int, len, sizeof(*qos));
|
||||
if (copy_to_user(optval, qos, len))
|
||||
if (copy_to_iter(qos, len, &opt->iter_out) != len)
|
||||
err = -EFAULT;
|
||||
|
||||
break;
|
||||
@@ -1936,9 +1938,8 @@ static int iso_sock_getsockopt(struct socket *sock, int level, int optname,
|
||||
}
|
||||
|
||||
len = min_t(unsigned int, len, base_len);
|
||||
if (copy_to_user(optval, base, len))
|
||||
err = -EFAULT;
|
||||
if (put_user(len, optlen))
|
||||
opt->optlen = len;
|
||||
if (copy_to_iter(base, len, &opt->iter_out) != len)
|
||||
err = -EFAULT;
|
||||
|
||||
break;
|
||||
@@ -2705,7 +2706,7 @@ static const struct proto_ops iso_sock_ops = {
|
||||
.socketpair = sock_no_socketpair,
|
||||
.shutdown = iso_sock_shutdown,
|
||||
.setsockopt = iso_sock_setsockopt,
|
||||
.getsockopt = iso_sock_getsockopt
|
||||
.getsockopt_iter = iso_sock_getsockopt
|
||||
};
|
||||
|
||||
static const struct net_proto_family iso_sock_family_ops = {
|
||||
|
||||
+26
-18
@@ -1,3 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
BlueZ - Bluetooth protocol stack for Linux
|
||||
Copyright (C) 2000-2001 Qualcomm Incorporated
|
||||
@@ -8,10 +9,6 @@
|
||||
|
||||
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.
|
||||
@@ -411,7 +408,7 @@ static void l2cap_chan_timeout(struct work_struct *work)
|
||||
|
||||
BT_DBG("chan %p state %s", chan, state_to_string(chan->state));
|
||||
|
||||
if (!conn) {
|
||||
if (test_bit(FLAG_DEL, &chan->flags)) {
|
||||
l2cap_chan_put(chan);
|
||||
return;
|
||||
}
|
||||
@@ -422,6 +419,9 @@ static void l2cap_chan_timeout(struct work_struct *work)
|
||||
*/
|
||||
l2cap_chan_lock(chan);
|
||||
|
||||
if (test_bit(FLAG_DEL, &chan->flags))
|
||||
goto unlock;
|
||||
|
||||
if (chan->state == BT_CONNECTED || chan->state == BT_CONFIG)
|
||||
reason = ECONNREFUSED;
|
||||
else if (chan->state == BT_CONNECT &&
|
||||
@@ -434,10 +434,10 @@ static void l2cap_chan_timeout(struct work_struct *work)
|
||||
|
||||
chan->ops->close(chan);
|
||||
|
||||
unlock:
|
||||
l2cap_chan_unlock(chan);
|
||||
l2cap_chan_put(chan);
|
||||
|
||||
mutex_unlock(&conn->lock);
|
||||
l2cap_chan_put(chan);
|
||||
}
|
||||
|
||||
struct l2cap_chan *l2cap_chan_create(void)
|
||||
@@ -490,6 +490,9 @@ static void l2cap_chan_destroy(struct kref *kref)
|
||||
list_del(&chan->global_l);
|
||||
write_unlock(&chan_list_lock);
|
||||
|
||||
if (chan->conn)
|
||||
l2cap_conn_put(chan->conn);
|
||||
|
||||
kfree(chan);
|
||||
}
|
||||
|
||||
@@ -593,7 +596,7 @@ void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
|
||||
|
||||
conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM;
|
||||
|
||||
chan->conn = conn;
|
||||
chan->conn = l2cap_conn_get(conn);
|
||||
|
||||
switch (chan->chan_type) {
|
||||
case L2CAP_CHAN_CONN_ORIENTED:
|
||||
@@ -648,30 +651,26 @@ void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
|
||||
|
||||
void l2cap_chan_del(struct l2cap_chan *chan, int err)
|
||||
{
|
||||
struct l2cap_conn *conn = chan->conn;
|
||||
|
||||
__clear_chan_timer(chan);
|
||||
|
||||
BT_DBG("chan %p, conn %p, err %d, state %s", chan, conn, err,
|
||||
BT_DBG("chan %p, err %d, state %s", chan, err,
|
||||
state_to_string(chan->state));
|
||||
|
||||
chan->ops->teardown(chan, err);
|
||||
|
||||
if (conn) {
|
||||
if (!test_and_set_bit(FLAG_DEL, &chan->flags)) {
|
||||
/* Delete from channel list */
|
||||
list_del(&chan->list);
|
||||
|
||||
l2cap_chan_put(chan);
|
||||
|
||||
chan->conn = NULL;
|
||||
|
||||
/* Reference was only held for non-fixed channels or
|
||||
* fixed channels that explicitly requested it using the
|
||||
* FLAG_HOLD_HCI_CONN flag.
|
||||
*/
|
||||
if (chan->chan_type != L2CAP_CHAN_FIXED ||
|
||||
test_bit(FLAG_HOLD_HCI_CONN, &chan->flags))
|
||||
hci_conn_drop(conn->hcon);
|
||||
hci_conn_drop(chan->conn->hcon);
|
||||
}
|
||||
|
||||
if (test_bit(CONF_NOT_COMPLETE, &chan->conf_state))
|
||||
@@ -1903,7 +1902,7 @@ static void l2cap_monitor_timeout(struct work_struct *work)
|
||||
|
||||
l2cap_chan_lock(chan);
|
||||
|
||||
if (!chan->conn) {
|
||||
if (test_bit(FLAG_DEL, &chan->flags)) {
|
||||
l2cap_chan_unlock(chan);
|
||||
l2cap_chan_put(chan);
|
||||
return;
|
||||
@@ -1924,7 +1923,7 @@ static void l2cap_retrans_timeout(struct work_struct *work)
|
||||
|
||||
l2cap_chan_lock(chan);
|
||||
|
||||
if (!chan->conn) {
|
||||
if (test_bit(FLAG_DEL, &chan->flags)) {
|
||||
l2cap_chan_unlock(chan);
|
||||
l2cap_chan_put(chan);
|
||||
return;
|
||||
@@ -2565,7 +2564,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
|
||||
int err;
|
||||
struct sk_buff_head seg_queue;
|
||||
|
||||
if (!chan->conn)
|
||||
if (test_bit(FLAG_DEL, &chan->flags))
|
||||
return -ENOTCONN;
|
||||
|
||||
/* Connectionless channel */
|
||||
@@ -3160,12 +3159,16 @@ static void l2cap_ack_timeout(struct work_struct *work)
|
||||
|
||||
l2cap_chan_lock(chan);
|
||||
|
||||
if (test_bit(FLAG_DEL, &chan->flags))
|
||||
goto unlock;
|
||||
|
||||
frames_to_ack = __seq_offset(chan, chan->buffer_seq,
|
||||
chan->last_acked_seq);
|
||||
|
||||
if (frames_to_ack)
|
||||
l2cap_send_rr_or_rnr(chan, 0);
|
||||
|
||||
unlock:
|
||||
l2cap_chan_unlock(chan);
|
||||
l2cap_chan_put(chan);
|
||||
}
|
||||
@@ -7026,6 +7029,11 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||
break;
|
||||
|
||||
case L2CAP_CID_CONN_LESS:
|
||||
if (skb->len < L2CAP_PSMLEN_SIZE) {
|
||||
kfree_skb(skb);
|
||||
break;
|
||||
}
|
||||
|
||||
psm = get_unaligned((__le16 *) skb->data);
|
||||
skb_pull(skb, L2CAP_PSMLEN_SIZE);
|
||||
l2cap_conless_channel(conn, psm, skb);
|
||||
|
||||
+38
-28
@@ -1,3 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
BlueZ - Bluetooth protocol stack for Linux
|
||||
Copyright (C) 2000-2001 Qualcomm Incorporated
|
||||
@@ -7,10 +8,6 @@
|
||||
|
||||
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.
|
||||
@@ -31,6 +28,7 @@
|
||||
#include <linux/export.h>
|
||||
#include <linux/filter.h>
|
||||
#include <linux/sched/signal.h>
|
||||
#include <linux/uio.h>
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
@@ -438,7 +436,7 @@ static int l2cap_get_mode(struct l2cap_chan *chan)
|
||||
}
|
||||
|
||||
static int l2cap_sock_getsockopt_old(struct socket *sock, int optname,
|
||||
char __user *optval, int __user *optlen)
|
||||
sockopt_t *sopt)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
|
||||
@@ -450,8 +448,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname,
|
||||
|
||||
BT_DBG("sk %p", sk);
|
||||
|
||||
if (get_user(len, optlen))
|
||||
return -EFAULT;
|
||||
len = sopt->optlen;
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
@@ -493,7 +490,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname,
|
||||
BT_DBG("mode 0x%2.2x", chan->mode);
|
||||
|
||||
len = min(len, sizeof(opts));
|
||||
if (copy_to_user(optval, (char *) &opts, len))
|
||||
if (copy_to_iter(&opts, len, &sopt->iter_out) != len)
|
||||
err = -EFAULT;
|
||||
|
||||
break;
|
||||
@@ -525,7 +522,8 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname,
|
||||
if (test_bit(FLAG_FORCE_RELIABLE, &chan->flags))
|
||||
opt |= L2CAP_LM_RELIABLE;
|
||||
|
||||
if (put_user(opt, (u32 __user *) optval))
|
||||
if (copy_to_iter(&opt, sizeof(opt), &sopt->iter_out) !=
|
||||
sizeof(opt))
|
||||
err = -EFAULT;
|
||||
|
||||
break;
|
||||
@@ -543,7 +541,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname,
|
||||
memcpy(cinfo.dev_class, chan->conn->hcon->dev_class, 3);
|
||||
|
||||
len = min(len, sizeof(cinfo));
|
||||
if (copy_to_user(optval, (char *) &cinfo, len))
|
||||
if (copy_to_iter(&cinfo, len, &sopt->iter_out) != len)
|
||||
err = -EFAULT;
|
||||
|
||||
break;
|
||||
@@ -558,25 +556,26 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname,
|
||||
}
|
||||
|
||||
static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
|
||||
char __user *optval, int __user *optlen)
|
||||
sockopt_t *sopt)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
|
||||
struct bt_security sec;
|
||||
struct bt_power pwr;
|
||||
u32 phys;
|
||||
int len, mode, err = 0;
|
||||
u32 opt;
|
||||
u16 mtu;
|
||||
u8 mval;
|
||||
|
||||
BT_DBG("sk %p", sk);
|
||||
|
||||
if (level == SOL_L2CAP)
|
||||
return l2cap_sock_getsockopt_old(sock, optname, optval, optlen);
|
||||
return l2cap_sock_getsockopt_old(sock, optname, sopt);
|
||||
|
||||
if (level != SOL_BLUETOOTH)
|
||||
return -ENOPROTOOPT;
|
||||
|
||||
if (get_user(len, optlen))
|
||||
return -EFAULT;
|
||||
len = sopt->optlen;
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
@@ -600,7 +599,7 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
|
||||
}
|
||||
|
||||
len = min_t(unsigned int, len, sizeof(sec));
|
||||
if (copy_to_user(optval, (char *) &sec, len))
|
||||
if (copy_to_iter(&sec, len, &sopt->iter_out) != len)
|
||||
err = -EFAULT;
|
||||
|
||||
break;
|
||||
@@ -611,15 +610,17 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
|
||||
break;
|
||||
}
|
||||
|
||||
if (put_user(test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags),
|
||||
(u32 __user *) optval))
|
||||
opt = test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags);
|
||||
if (copy_to_iter(&opt, sizeof(opt), &sopt->iter_out) !=
|
||||
sizeof(opt))
|
||||
err = -EFAULT;
|
||||
|
||||
break;
|
||||
|
||||
case BT_FLUSHABLE:
|
||||
if (put_user(test_bit(FLAG_FLUSHABLE, &chan->flags),
|
||||
(u32 __user *) optval))
|
||||
opt = test_bit(FLAG_FLUSHABLE, &chan->flags);
|
||||
if (copy_to_iter(&opt, sizeof(opt), &sopt->iter_out) !=
|
||||
sizeof(opt))
|
||||
err = -EFAULT;
|
||||
|
||||
break;
|
||||
@@ -634,13 +635,15 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
|
||||
pwr.force_active = test_bit(FLAG_FORCE_ACTIVE, &chan->flags);
|
||||
|
||||
len = min_t(unsigned int, len, sizeof(pwr));
|
||||
if (copy_to_user(optval, (char *) &pwr, len))
|
||||
if (copy_to_iter(&pwr, len, &sopt->iter_out) != len)
|
||||
err = -EFAULT;
|
||||
|
||||
break;
|
||||
|
||||
case BT_CHANNEL_POLICY:
|
||||
if (put_user(chan->chan_policy, (u32 __user *) optval))
|
||||
opt = chan->chan_policy;
|
||||
if (copy_to_iter(&opt, sizeof(opt), &sopt->iter_out) !=
|
||||
sizeof(opt))
|
||||
err = -EFAULT;
|
||||
break;
|
||||
|
||||
@@ -655,7 +658,9 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
|
||||
break;
|
||||
}
|
||||
|
||||
if (put_user(chan->omtu, (u16 __user *) optval))
|
||||
mtu = chan->omtu;
|
||||
if (copy_to_iter(&mtu, sizeof(mtu), &sopt->iter_out) !=
|
||||
sizeof(mtu))
|
||||
err = -EFAULT;
|
||||
break;
|
||||
|
||||
@@ -665,7 +670,9 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
|
||||
break;
|
||||
}
|
||||
|
||||
if (put_user(chan->imtu, (u16 __user *) optval))
|
||||
mtu = chan->imtu;
|
||||
if (copy_to_iter(&mtu, sizeof(mtu), &sopt->iter_out) !=
|
||||
sizeof(mtu))
|
||||
err = -EFAULT;
|
||||
break;
|
||||
|
||||
@@ -675,9 +682,10 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
|
||||
break;
|
||||
}
|
||||
|
||||
phys = hci_conn_get_phy(chan->conn->hcon);
|
||||
opt = hci_conn_get_phy(chan->conn->hcon);
|
||||
|
||||
if (put_user(phys, (u32 __user *) optval))
|
||||
if (copy_to_iter(&opt, sizeof(opt), &sopt->iter_out) !=
|
||||
sizeof(opt))
|
||||
err = -EFAULT;
|
||||
break;
|
||||
|
||||
@@ -698,7 +706,9 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
|
||||
break;
|
||||
}
|
||||
|
||||
if (put_user(mode, (u8 __user *) optval))
|
||||
mval = mode;
|
||||
if (copy_to_iter(&mval, sizeof(mval), &sopt->iter_out) !=
|
||||
sizeof(mval))
|
||||
err = -EFAULT;
|
||||
break;
|
||||
|
||||
@@ -2039,7 +2049,7 @@ static const struct proto_ops l2cap_sock_ops = {
|
||||
.socketpair = sock_no_socketpair,
|
||||
.shutdown = l2cap_sock_shutdown,
|
||||
.setsockopt = l2cap_sock_setsockopt,
|
||||
.getsockopt = l2cap_sock_getsockopt
|
||||
.getsockopt_iter = l2cap_sock_getsockopt
|
||||
};
|
||||
|
||||
static const struct net_proto_family l2cap_sock_family_ops = {
|
||||
|
||||
+1
-4
@@ -1,13 +1,10 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
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.
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
BlueZ - Bluetooth protocol stack for Linux
|
||||
|
||||
Copyright (C) 2010 Nokia Corporation
|
||||
Copyright (C) 2011-2012 Intel Corporation
|
||||
|
||||
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.
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
BlueZ - Bluetooth protocol stack for Linux
|
||||
|
||||
Copyright (C) 2015 Intel Corporation
|
||||
|
||||
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.
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
BlueZ - Bluetooth protocol stack for Linux
|
||||
Copyright (C) 2015 Intel Coropration
|
||||
|
||||
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.
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
RFCOMM implementation for Linux Bluetooth stack (BlueZ).
|
||||
Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>
|
||||
Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org>
|
||||
|
||||
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.
|
||||
|
||||
+18
-17
@@ -1,12 +1,9 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
RFCOMM implementation for Linux Bluetooth stack (BlueZ).
|
||||
Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>
|
||||
Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org>
|
||||
|
||||
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.
|
||||
@@ -28,6 +25,7 @@
|
||||
#include <linux/export.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/sched/signal.h>
|
||||
#include <linux/uio.h>
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
@@ -740,7 +738,8 @@ static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname,
|
||||
return err;
|
||||
}
|
||||
|
||||
static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __user *optval, int __user *optlen)
|
||||
static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname,
|
||||
sockopt_t *sopt)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
struct sock *l2cap_sk;
|
||||
@@ -752,8 +751,7 @@ static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __u
|
||||
|
||||
BT_DBG("sk %p", sk);
|
||||
|
||||
if (get_user(len, optlen))
|
||||
return -EFAULT;
|
||||
len = sopt->optlen;
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
@@ -782,7 +780,8 @@ static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __u
|
||||
if (rfcomm_pi(sk)->role_switch)
|
||||
opt |= RFCOMM_LM_MASTER;
|
||||
|
||||
if (put_user(opt, (u32 __user *) optval))
|
||||
if (copy_to_iter(&opt, sizeof(opt), &sopt->iter_out) !=
|
||||
sizeof(opt))
|
||||
err = -EFAULT;
|
||||
|
||||
break;
|
||||
@@ -802,7 +801,7 @@ static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __u
|
||||
memcpy(cinfo.dev_class, conn->hcon->dev_class, 3);
|
||||
|
||||
len = min(len, sizeof(cinfo));
|
||||
if (copy_to_user(optval, (char *) &cinfo, len))
|
||||
if (copy_to_iter(&cinfo, len, &sopt->iter_out) != len)
|
||||
err = -EFAULT;
|
||||
|
||||
break;
|
||||
@@ -816,23 +815,24 @@ static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __u
|
||||
return err;
|
||||
}
|
||||
|
||||
static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen)
|
||||
static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname,
|
||||
sockopt_t *sopt)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
struct bt_security sec;
|
||||
int err = 0;
|
||||
size_t len;
|
||||
u32 opt;
|
||||
|
||||
BT_DBG("sk %p", sk);
|
||||
|
||||
if (level == SOL_RFCOMM)
|
||||
return rfcomm_sock_getsockopt_old(sock, optname, optval, optlen);
|
||||
return rfcomm_sock_getsockopt_old(sock, optname, sopt);
|
||||
|
||||
if (level != SOL_BLUETOOTH)
|
||||
return -ENOPROTOOPT;
|
||||
|
||||
if (get_user(len, optlen))
|
||||
return -EFAULT;
|
||||
len = sopt->optlen;
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
@@ -847,7 +847,7 @@ static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname, c
|
||||
sec.key_size = 0;
|
||||
|
||||
len = min(len, sizeof(sec));
|
||||
if (copy_to_user(optval, (char *) &sec, len))
|
||||
if (copy_to_iter(&sec, len, &sopt->iter_out) != len)
|
||||
err = -EFAULT;
|
||||
|
||||
break;
|
||||
@@ -858,8 +858,9 @@ static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname, c
|
||||
break;
|
||||
}
|
||||
|
||||
if (put_user(test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags),
|
||||
(u32 __user *) optval))
|
||||
opt = test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags);
|
||||
if (copy_to_iter(&opt, sizeof(opt), &sopt->iter_out) !=
|
||||
sizeof(opt))
|
||||
err = -EFAULT;
|
||||
|
||||
break;
|
||||
@@ -1039,7 +1040,7 @@ static const struct proto_ops rfcomm_sock_ops = {
|
||||
.recvmsg = rfcomm_sock_recvmsg,
|
||||
.shutdown = rfcomm_sock_shutdown,
|
||||
.setsockopt = rfcomm_sock_setsockopt,
|
||||
.getsockopt = rfcomm_sock_getsockopt,
|
||||
.getsockopt_iter = rfcomm_sock_getsockopt,
|
||||
.ioctl = rfcomm_sock_ioctl,
|
||||
.gettstamp = sock_gettstamp,
|
||||
.poll = bt_sock_poll,
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
RFCOMM implementation for Linux Bluetooth stack (BlueZ).
|
||||
Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>
|
||||
Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org>
|
||||
|
||||
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.
|
||||
|
||||
+32
-32
@@ -1,13 +1,10 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
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.
|
||||
@@ -28,6 +25,7 @@
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/sched/signal.h>
|
||||
#include <linux/uio.h>
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
@@ -1089,7 +1087,7 @@ static int sco_sock_setsockopt(struct socket *sock, int level, int optname,
|
||||
}
|
||||
|
||||
static int sco_sock_getsockopt_old(struct socket *sock, int optname,
|
||||
char __user *optval, int __user *optlen)
|
||||
sockopt_t *opt)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
struct sco_options opts;
|
||||
@@ -1099,8 +1097,7 @@ static int sco_sock_getsockopt_old(struct socket *sock, int optname,
|
||||
|
||||
BT_DBG("sk %p", sk);
|
||||
|
||||
if (get_user(len, optlen))
|
||||
return -EFAULT;
|
||||
len = opt->optlen;
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
@@ -1118,7 +1115,7 @@ static int sco_sock_getsockopt_old(struct socket *sock, int optname,
|
||||
BT_DBG("mtu %u", opts.mtu);
|
||||
|
||||
len = min(len, sizeof(opts));
|
||||
if (copy_to_user(optval, (char *)&opts, len))
|
||||
if (copy_to_iter(&opts, len, &opt->iter_out) != len)
|
||||
err = -EFAULT;
|
||||
|
||||
break;
|
||||
@@ -1136,7 +1133,7 @@ static int sco_sock_getsockopt_old(struct socket *sock, int optname,
|
||||
memcpy(cinfo.dev_class, sco_pi(sk)->conn->hcon->dev_class, 3);
|
||||
|
||||
len = min(len, sizeof(cinfo));
|
||||
if (copy_to_user(optval, (char *)&cinfo, len))
|
||||
if (copy_to_iter(&cinfo, len, &opt->iter_out) != len)
|
||||
err = -EFAULT;
|
||||
|
||||
break;
|
||||
@@ -1151,15 +1148,15 @@ static int sco_sock_getsockopt_old(struct socket *sock, int optname,
|
||||
}
|
||||
|
||||
static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
|
||||
char __user *optval, int __user *optlen)
|
||||
sockopt_t *opt)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
int len, err = 0;
|
||||
int len, val, err = 0;
|
||||
struct bt_voice voice;
|
||||
u32 phys;
|
||||
int buf_len;
|
||||
struct codec_list *c;
|
||||
u8 num_codecs, i, __user *ptr;
|
||||
u8 num_codecs, i;
|
||||
struct hci_dev *hdev;
|
||||
struct hci_codec_caps *caps;
|
||||
struct bt_codec codec;
|
||||
@@ -1167,10 +1164,9 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
|
||||
BT_DBG("sk %p", sk);
|
||||
|
||||
if (level == SOL_SCO)
|
||||
return sco_sock_getsockopt_old(sock, optname, optval, optlen);
|
||||
return sco_sock_getsockopt_old(sock, optname, opt);
|
||||
|
||||
if (get_user(len, optlen))
|
||||
return -EFAULT;
|
||||
len = opt->optlen;
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
@@ -1182,8 +1178,9 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
|
||||
break;
|
||||
}
|
||||
|
||||
if (put_user(test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags),
|
||||
(u32 __user *)optval))
|
||||
val = test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags);
|
||||
if (copy_to_iter(&val, sizeof(val), &opt->iter_out) !=
|
||||
sizeof(val))
|
||||
err = -EFAULT;
|
||||
|
||||
break;
|
||||
@@ -1192,7 +1189,7 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
|
||||
voice.setting = sco_pi(sk)->setting;
|
||||
|
||||
len = min_t(unsigned int, len, sizeof(voice));
|
||||
if (copy_to_user(optval, (char *)&voice, len))
|
||||
if (copy_to_iter(&voice, len, &opt->iter_out) != len)
|
||||
err = -EFAULT;
|
||||
|
||||
break;
|
||||
@@ -1205,13 +1202,15 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
|
||||
|
||||
phys = hci_conn_get_phy(sco_pi(sk)->conn->hcon);
|
||||
|
||||
if (put_user(phys, (u32 __user *) optval))
|
||||
if (copy_to_iter(&phys, sizeof(phys), &opt->iter_out) !=
|
||||
sizeof(phys))
|
||||
err = -EFAULT;
|
||||
break;
|
||||
|
||||
case BT_PKT_STATUS:
|
||||
if (put_user(test_bit(BT_SK_PKT_STATUS, &bt_sk(sk)->flags),
|
||||
(int __user *)optval))
|
||||
val = test_bit(BT_SK_PKT_STATUS, &bt_sk(sk)->flags);
|
||||
if (copy_to_iter(&val, sizeof(val), &opt->iter_out) !=
|
||||
sizeof(val))
|
||||
err = -EFAULT;
|
||||
break;
|
||||
|
||||
@@ -1222,7 +1221,9 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
|
||||
break;
|
||||
}
|
||||
|
||||
if (put_user(sco_pi(sk)->conn->mtu, (u32 __user *)optval))
|
||||
val = sco_pi(sk)->conn->mtu;
|
||||
if (copy_to_iter(&val, sizeof(val), &opt->iter_out) !=
|
||||
sizeof(val))
|
||||
err = -EFAULT;
|
||||
break;
|
||||
|
||||
@@ -1269,13 +1270,12 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
|
||||
hci_dev_put(hdev);
|
||||
return -ENOBUFS;
|
||||
}
|
||||
ptr = optval;
|
||||
|
||||
if (put_user(num_codecs, ptr)) {
|
||||
if (copy_to_iter(&num_codecs, sizeof(num_codecs),
|
||||
&opt->iter_out) != sizeof(num_codecs)) {
|
||||
hci_dev_put(hdev);
|
||||
return -EFAULT;
|
||||
}
|
||||
ptr += sizeof(num_codecs);
|
||||
|
||||
/* Iterate all the codecs supported over SCO and populate
|
||||
* codec data
|
||||
@@ -1292,11 +1292,11 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
|
||||
if (err < 0)
|
||||
break;
|
||||
codec.num_caps = c->num_caps;
|
||||
if (copy_to_user(ptr, &codec, sizeof(codec))) {
|
||||
if (copy_to_iter(&codec, sizeof(codec), &opt->iter_out)
|
||||
!= sizeof(codec)) {
|
||||
err = -EFAULT;
|
||||
break;
|
||||
}
|
||||
ptr += sizeof(codec);
|
||||
|
||||
/* find codec capabilities data length */
|
||||
len = 0;
|
||||
@@ -1306,11 +1306,11 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
|
||||
}
|
||||
|
||||
/* copy codec capabilities data */
|
||||
if (len && copy_to_user(ptr, c->caps, len)) {
|
||||
if (len &&
|
||||
copy_to_iter(c->caps, len, &opt->iter_out) != len) {
|
||||
err = -EFAULT;
|
||||
break;
|
||||
}
|
||||
ptr += len;
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
@@ -1318,8 +1318,8 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname,
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
if (!err && put_user(buf_len, optlen))
|
||||
err = -EFAULT;
|
||||
if (!err)
|
||||
opt->optlen = buf_len;
|
||||
|
||||
break;
|
||||
|
||||
@@ -1594,7 +1594,7 @@ static const struct proto_ops sco_sock_ops = {
|
||||
.socketpair = sock_no_socketpair,
|
||||
.shutdown = sco_sock_shutdown,
|
||||
.setsockopt = sco_sock_setsockopt,
|
||||
.getsockopt = sco_sock_getsockopt
|
||||
.getsockopt_iter = sco_sock_getsockopt
|
||||
};
|
||||
|
||||
static const struct net_proto_family sco_sock_family_ops = {
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
BlueZ - Bluetooth protocol stack for Linux
|
||||
|
||||
Copyright (C) 2014 Intel Corporation
|
||||
|
||||
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.
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
BlueZ - Bluetooth protocol stack for Linux
|
||||
Copyright (C) 2014 Intel Corporation
|
||||
|
||||
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.
|
||||
|
||||
+65
-120
@@ -1,11 +1,8 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
BlueZ - Bluetooth protocol stack for Linux
|
||||
Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
|
||||
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.
|
||||
@@ -22,8 +19,8 @@
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <crypto/aes-cbc-macs.h>
|
||||
#include <crypto/aes.h>
|
||||
#include <crypto/hash.h>
|
||||
#include <crypto/kpp.h>
|
||||
#include <crypto/utils.h>
|
||||
|
||||
@@ -63,7 +60,7 @@
|
||||
0x3f : 0x07)
|
||||
#define KEY_DIST_MASK 0x07
|
||||
|
||||
/* Maximum message length that can be passed to aes_cmac */
|
||||
/* Maximum message length that can be passed to smp_aes_cmac */
|
||||
#define CMAC_MSG_MAX 80
|
||||
|
||||
enum {
|
||||
@@ -89,7 +86,6 @@ struct smp_dev {
|
||||
u8 local_rand[16];
|
||||
bool debug_key;
|
||||
|
||||
struct crypto_shash *tfm_cmac;
|
||||
struct crypto_kpp *tfm_ecdh;
|
||||
};
|
||||
|
||||
@@ -127,7 +123,6 @@ struct smp_chan {
|
||||
u8 dhkey[32];
|
||||
u8 mackey[16];
|
||||
|
||||
struct crypto_shash *tfm_cmac;
|
||||
struct crypto_kpp *tfm_ecdh;
|
||||
};
|
||||
|
||||
@@ -166,20 +161,15 @@ static inline void swap_buf(const u8 *src, u8 *dst, size_t len)
|
||||
* AES-CMAC, f4, f5, f6, g2 and h6.
|
||||
*/
|
||||
|
||||
static int aes_cmac(struct crypto_shash *tfm, const u8 k[16], const u8 *m,
|
||||
size_t len, u8 mac[16])
|
||||
static int smp_aes_cmac(const u8 k[16], const u8 *m, size_t len, u8 mac[16])
|
||||
{
|
||||
uint8_t tmp[16], mac_msb[16], msg_msb[CMAC_MSG_MAX];
|
||||
struct aes_cmac_key key;
|
||||
int err;
|
||||
|
||||
if (len > CMAC_MSG_MAX)
|
||||
return -EFBIG;
|
||||
|
||||
if (!tfm) {
|
||||
BT_ERR("tfm %p", tfm);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Swap key and message from LSB to MSB */
|
||||
swap_buf(k, tmp, 16);
|
||||
swap_buf(m, msg_msb, len);
|
||||
@@ -187,17 +177,10 @@ static int aes_cmac(struct crypto_shash *tfm, const u8 k[16], const u8 *m,
|
||||
SMP_DBG("msg (len %zu) %*phN", len, (int) len, m);
|
||||
SMP_DBG("key %16phN", k);
|
||||
|
||||
err = crypto_shash_setkey(tfm, tmp, 16);
|
||||
if (err) {
|
||||
BT_ERR("cipher setkey failed: %d", err);
|
||||
err = aes_cmac_preparekey(&key, tmp, 16);
|
||||
if (WARN_ON_ONCE(err)) /* Should never happen, as 16 is valid keylen */
|
||||
return err;
|
||||
}
|
||||
|
||||
err = crypto_shash_tfm_digest(tfm, msg_msb, len, mac_msb);
|
||||
if (err) {
|
||||
BT_ERR("Hash computation error %d", err);
|
||||
return err;
|
||||
}
|
||||
aes_cmac(&key, msg_msb, len, mac_msb);
|
||||
|
||||
swap_buf(mac_msb, mac, 16);
|
||||
|
||||
@@ -206,8 +189,8 @@ static int aes_cmac(struct crypto_shash *tfm, const u8 k[16], const u8 *m,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int smp_f4(struct crypto_shash *tfm_cmac, const u8 u[32],
|
||||
const u8 v[32], const u8 x[16], u8 z, u8 res[16])
|
||||
static int smp_f4(const u8 u[32], const u8 v[32], const u8 x[16], u8 z,
|
||||
u8 res[16])
|
||||
{
|
||||
u8 m[65];
|
||||
int err;
|
||||
@@ -220,7 +203,7 @@ static int smp_f4(struct crypto_shash *tfm_cmac, const u8 u[32],
|
||||
memcpy(m + 1, v, 32);
|
||||
memcpy(m + 33, u, 32);
|
||||
|
||||
err = aes_cmac(tfm_cmac, x, m, sizeof(m), res);
|
||||
err = smp_aes_cmac(x, m, sizeof(m), res);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -229,9 +212,8 @@ static int smp_f4(struct crypto_shash *tfm_cmac, const u8 u[32],
|
||||
return err;
|
||||
}
|
||||
|
||||
static int smp_f5(struct crypto_shash *tfm_cmac, const u8 w[32],
|
||||
const u8 n1[16], const u8 n2[16], const u8 a1[7],
|
||||
const u8 a2[7], u8 mackey[16], u8 ltk[16])
|
||||
static int smp_f5(const u8 w[32], const u8 n1[16], const u8 n2[16],
|
||||
const u8 a1[7], const u8 a2[7], u8 mackey[16], u8 ltk[16])
|
||||
{
|
||||
/* The btle, salt and length "magic" values are as defined in
|
||||
* the SMP section of the Bluetooth core specification. In ASCII
|
||||
@@ -250,7 +232,7 @@ static int smp_f5(struct crypto_shash *tfm_cmac, const u8 w[32],
|
||||
SMP_DBG("n1 %16phN n2 %16phN", n1, n2);
|
||||
SMP_DBG("a1 %7phN a2 %7phN", a1, a2);
|
||||
|
||||
err = aes_cmac(tfm_cmac, salt, w, 32, t);
|
||||
err = smp_aes_cmac(salt, w, 32, t);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -265,7 +247,7 @@ static int smp_f5(struct crypto_shash *tfm_cmac, const u8 w[32],
|
||||
|
||||
m[52] = 0; /* Counter */
|
||||
|
||||
err = aes_cmac(tfm_cmac, t, m, sizeof(m), mackey);
|
||||
err = smp_aes_cmac(t, m, sizeof(m), mackey);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -273,7 +255,7 @@ static int smp_f5(struct crypto_shash *tfm_cmac, const u8 w[32],
|
||||
|
||||
m[52] = 1; /* Counter */
|
||||
|
||||
err = aes_cmac(tfm_cmac, t, m, sizeof(m), ltk);
|
||||
err = smp_aes_cmac(t, m, sizeof(m), ltk);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -282,10 +264,9 @@ static int smp_f5(struct crypto_shash *tfm_cmac, const u8 w[32],
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int smp_f6(struct crypto_shash *tfm_cmac, const u8 w[16],
|
||||
const u8 n1[16], const u8 n2[16], const u8 r[16],
|
||||
const u8 io_cap[3], const u8 a1[7], const u8 a2[7],
|
||||
u8 res[16])
|
||||
static int smp_f6(const u8 w[16], const u8 n1[16], const u8 n2[16],
|
||||
const u8 r[16], const u8 io_cap[3], const u8 a1[7],
|
||||
const u8 a2[7], u8 res[16])
|
||||
{
|
||||
u8 m[65];
|
||||
int err;
|
||||
@@ -301,7 +282,7 @@ static int smp_f6(struct crypto_shash *tfm_cmac, const u8 w[16],
|
||||
memcpy(m + 33, n2, 16);
|
||||
memcpy(m + 49, n1, 16);
|
||||
|
||||
err = aes_cmac(tfm_cmac, w, m, sizeof(m), res);
|
||||
err = smp_aes_cmac(w, m, sizeof(m), res);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -310,8 +291,8 @@ static int smp_f6(struct crypto_shash *tfm_cmac, const u8 w[16],
|
||||
return err;
|
||||
}
|
||||
|
||||
static int smp_g2(struct crypto_shash *tfm_cmac, const u8 u[32], const u8 v[32],
|
||||
const u8 x[16], const u8 y[16], u32 *val)
|
||||
static int smp_g2(const u8 u[32], const u8 v[32], const u8 x[16],
|
||||
const u8 y[16], u32 *val)
|
||||
{
|
||||
u8 m[80], tmp[16];
|
||||
int err;
|
||||
@@ -324,7 +305,7 @@ static int smp_g2(struct crypto_shash *tfm_cmac, const u8 u[32], const u8 v[32],
|
||||
memcpy(m + 16, v, 32);
|
||||
memcpy(m + 48, u, 32);
|
||||
|
||||
err = aes_cmac(tfm_cmac, x, m, sizeof(m), tmp);
|
||||
err = smp_aes_cmac(x, m, sizeof(m), tmp);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -336,14 +317,13 @@ static int smp_g2(struct crypto_shash *tfm_cmac, const u8 u[32], const u8 v[32],
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int smp_h6(struct crypto_shash *tfm_cmac, const u8 w[16],
|
||||
const u8 key_id[4], u8 res[16])
|
||||
static int smp_h6(const u8 w[16], const u8 key_id[4], u8 res[16])
|
||||
{
|
||||
int err;
|
||||
|
||||
SMP_DBG("w %16phN key_id %4phN", w, key_id);
|
||||
|
||||
err = aes_cmac(tfm_cmac, w, key_id, 4, res);
|
||||
err = smp_aes_cmac(w, key_id, 4, res);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -352,14 +332,13 @@ static int smp_h6(struct crypto_shash *tfm_cmac, const u8 w[16],
|
||||
return err;
|
||||
}
|
||||
|
||||
static int smp_h7(struct crypto_shash *tfm_cmac, const u8 w[16],
|
||||
const u8 salt[16], u8 res[16])
|
||||
static int smp_h7(const u8 w[16], const u8 salt[16], u8 res[16])
|
||||
{
|
||||
int err;
|
||||
|
||||
SMP_DBG("w %16phN salt %16phN", w, salt);
|
||||
|
||||
err = aes_cmac(tfm_cmac, salt, w, 16, res);
|
||||
err = smp_aes_cmac(salt, w, 16, res);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -574,8 +553,7 @@ int smp_generate_oob(struct hci_dev *hdev, u8 hash[16], u8 rand[16])
|
||||
|
||||
get_random_bytes(smp->local_rand, 16);
|
||||
|
||||
err = smp_f4(smp->tfm_cmac, smp->local_pk, smp->local_pk,
|
||||
smp->local_rand, 0, hash);
|
||||
err = smp_f4(smp->local_pk, smp->local_pk, smp->local_rand, 0, hash);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
@@ -757,7 +735,6 @@ static void smp_chan_destroy(struct l2cap_conn *conn)
|
||||
kfree_sensitive(smp->responder_csrk);
|
||||
kfree_sensitive(smp->link_key);
|
||||
|
||||
crypto_free_shash(smp->tfm_cmac);
|
||||
crypto_free_kpp(smp->tfm_ecdh);
|
||||
|
||||
/* Ensure that we don't leave any debug key around if debug key
|
||||
@@ -1160,7 +1137,7 @@ static void sc_generate_link_key(struct smp_chan *smp)
|
||||
/* SALT = 0x000000000000000000000000746D7031 */
|
||||
const u8 salt[16] = { 0x31, 0x70, 0x6d, 0x74 };
|
||||
|
||||
if (smp_h7(smp->tfm_cmac, smp->tk, salt, smp->link_key)) {
|
||||
if (smp_h7(smp->tk, salt, smp->link_key)) {
|
||||
kfree_sensitive(smp->link_key);
|
||||
smp->link_key = NULL;
|
||||
return;
|
||||
@@ -1169,14 +1146,14 @@ static void sc_generate_link_key(struct smp_chan *smp)
|
||||
/* From core spec. Spells out in ASCII as 'tmp1'. */
|
||||
const u8 tmp1[4] = { 0x31, 0x70, 0x6d, 0x74 };
|
||||
|
||||
if (smp_h6(smp->tfm_cmac, smp->tk, tmp1, smp->link_key)) {
|
||||
if (smp_h6(smp->tk, tmp1, smp->link_key)) {
|
||||
kfree_sensitive(smp->link_key);
|
||||
smp->link_key = NULL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (smp_h6(smp->tfm_cmac, smp->link_key, lebr, smp->link_key)) {
|
||||
if (smp_h6(smp->link_key, lebr, smp->link_key)) {
|
||||
kfree_sensitive(smp->link_key);
|
||||
smp->link_key = NULL;
|
||||
return;
|
||||
@@ -1218,17 +1195,17 @@ static void sc_generate_ltk(struct smp_chan *smp)
|
||||
/* SALT = 0x000000000000000000000000746D7032 */
|
||||
const u8 salt[16] = { 0x32, 0x70, 0x6d, 0x74 };
|
||||
|
||||
if (smp_h7(smp->tfm_cmac, key->val, salt, smp->tk))
|
||||
if (smp_h7(key->val, salt, smp->tk))
|
||||
return;
|
||||
} else {
|
||||
/* From core spec. Spells out in ASCII as 'tmp2'. */
|
||||
const u8 tmp2[4] = { 0x32, 0x70, 0x6d, 0x74 };
|
||||
|
||||
if (smp_h6(smp->tfm_cmac, key->val, tmp2, smp->tk))
|
||||
if (smp_h6(key->val, tmp2, smp->tk))
|
||||
return;
|
||||
}
|
||||
|
||||
if (smp_h6(smp->tfm_cmac, smp->tk, brle, smp->tk))
|
||||
if (smp_h6(smp->tk, brle, smp->tk))
|
||||
return;
|
||||
|
||||
sc_add_ltk(smp);
|
||||
@@ -1389,16 +1366,10 @@ static struct smp_chan *smp_chan_create(struct l2cap_conn *conn)
|
||||
if (!smp)
|
||||
return NULL;
|
||||
|
||||
smp->tfm_cmac = crypto_alloc_shash("cmac(aes)", 0, 0);
|
||||
if (IS_ERR(smp->tfm_cmac)) {
|
||||
bt_dev_err(hcon->hdev, "Unable to create CMAC crypto context");
|
||||
goto zfree_smp;
|
||||
}
|
||||
|
||||
smp->tfm_ecdh = crypto_alloc_kpp("ecdh-nist-p256", 0, 0);
|
||||
if (IS_ERR(smp->tfm_ecdh)) {
|
||||
bt_dev_err(hcon->hdev, "Unable to create ECDH crypto context");
|
||||
goto free_shash;
|
||||
goto zfree_smp;
|
||||
}
|
||||
|
||||
smp->conn = conn;
|
||||
@@ -1412,8 +1383,6 @@ static struct smp_chan *smp_chan_create(struct l2cap_conn *conn)
|
||||
|
||||
return smp;
|
||||
|
||||
free_shash:
|
||||
crypto_free_shash(smp->tfm_cmac);
|
||||
zfree_smp:
|
||||
kfree_sensitive(smp);
|
||||
return NULL;
|
||||
@@ -1437,7 +1406,7 @@ static int sc_mackey_and_ltk(struct smp_chan *smp, u8 mackey[16], u8 ltk[16])
|
||||
a[6] = hcon->init_addr_type;
|
||||
b[6] = hcon->resp_addr_type;
|
||||
|
||||
return smp_f5(smp->tfm_cmac, smp->dhkey, na, nb, a, b, mackey, ltk);
|
||||
return smp_f5(smp->dhkey, na, nb, a, b, mackey, ltk);
|
||||
}
|
||||
|
||||
static void sc_dhkey_check(struct smp_chan *smp)
|
||||
@@ -1470,8 +1439,8 @@ static void sc_dhkey_check(struct smp_chan *smp)
|
||||
if (smp->method == REQ_OOB)
|
||||
memcpy(r, smp->rr, 16);
|
||||
|
||||
smp_f6(smp->tfm_cmac, smp->mackey, smp->prnd, smp->rrnd, r, io_cap,
|
||||
local_addr, remote_addr, check.e);
|
||||
smp_f6(smp->mackey, smp->prnd, smp->rrnd, r, io_cap, local_addr,
|
||||
remote_addr, check.e);
|
||||
|
||||
smp_send_cmd(smp->conn, SMP_CMD_DHKEY_CHECK, sizeof(check), &check);
|
||||
}
|
||||
@@ -1488,7 +1457,7 @@ static u8 sc_passkey_send_confirm(struct smp_chan *smp)
|
||||
|
||||
get_random_bytes(smp->prnd, sizeof(smp->prnd));
|
||||
|
||||
if (smp_f4(smp->tfm_cmac, smp->local_pk, smp->remote_pk, smp->prnd, r,
|
||||
if (smp_f4(smp->local_pk, smp->remote_pk, smp->prnd, r,
|
||||
cfm.confirm_val))
|
||||
return SMP_UNSPECIFIED;
|
||||
|
||||
@@ -1513,8 +1482,7 @@ static u8 sc_passkey_round(struct smp_chan *smp, u8 smp_op)
|
||||
r = ((hcon->passkey_notify >> smp->passkey_round) & 0x01);
|
||||
r |= 0x80;
|
||||
|
||||
if (smp_f4(smp->tfm_cmac, smp->remote_pk, smp->local_pk,
|
||||
smp->rrnd, r, cfm))
|
||||
if (smp_f4(smp->remote_pk, smp->local_pk, smp->rrnd, r, cfm))
|
||||
return SMP_UNSPECIFIED;
|
||||
|
||||
if (crypto_memneq(smp->pcnf, cfm, 16))
|
||||
@@ -2178,8 +2146,7 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||
if (test_bit(SMP_FLAG_INITIATOR, &smp->flags)) {
|
||||
u8 cfm[16];
|
||||
|
||||
err = smp_f4(smp->tfm_cmac, smp->remote_pk, smp->local_pk,
|
||||
smp->rrnd, 0, cfm);
|
||||
err = smp_f4(smp->remote_pk, smp->local_pk, smp->rrnd, 0, cfm);
|
||||
if (err)
|
||||
return SMP_UNSPECIFIED;
|
||||
|
||||
@@ -2205,7 +2172,7 @@ mackey_and_ltk:
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = smp_g2(smp->tfm_cmac, pkax, pkbx, na, nb, &passkey);
|
||||
err = smp_g2(pkax, pkbx, na, nb, &passkey);
|
||||
if (err)
|
||||
return SMP_UNSPECIFIED;
|
||||
|
||||
@@ -2750,8 +2717,8 @@ static int smp_cmd_public_key(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||
memcpy(smp->remote_pk, key, 64);
|
||||
|
||||
if (test_bit(SMP_FLAG_REMOTE_OOB, &smp->flags)) {
|
||||
err = smp_f4(smp->tfm_cmac, smp->remote_pk, smp->remote_pk,
|
||||
smp->rr, 0, cfm.confirm_val);
|
||||
err = smp_f4(smp->remote_pk, smp->remote_pk, smp->rr, 0,
|
||||
cfm.confirm_val);
|
||||
if (err)
|
||||
return SMP_UNSPECIFIED;
|
||||
|
||||
@@ -2851,8 +2818,8 @@ static int smp_cmd_public_key(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||
if (test_bit(SMP_FLAG_INITIATOR, &smp->flags))
|
||||
return 0;
|
||||
|
||||
err = smp_f4(smp->tfm_cmac, smp->local_pk, smp->remote_pk, smp->prnd,
|
||||
0, cfm.confirm_val);
|
||||
err = smp_f4(smp->local_pk, smp->remote_pk, smp->prnd, 0,
|
||||
cfm.confirm_val);
|
||||
if (err)
|
||||
return SMP_UNSPECIFIED;
|
||||
|
||||
@@ -2899,8 +2866,8 @@ static int smp_cmd_dhkey_check(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||
else if (smp->method == REQ_OOB)
|
||||
memcpy(r, smp->lr, 16);
|
||||
|
||||
err = smp_f6(smp->tfm_cmac, smp->mackey, smp->rrnd, smp->prnd, r,
|
||||
io_cap, remote_addr, local_addr, e);
|
||||
err = smp_f6(smp->mackey, smp->rrnd, smp->prnd, r, io_cap, remote_addr,
|
||||
local_addr, e);
|
||||
if (err)
|
||||
return SMP_UNSPECIFIED;
|
||||
|
||||
@@ -3286,7 +3253,6 @@ static struct l2cap_chan *smp_add_cid(struct hci_dev *hdev, u16 cid)
|
||||
{
|
||||
struct l2cap_chan *chan;
|
||||
struct smp_dev *smp;
|
||||
struct crypto_shash *tfm_cmac;
|
||||
struct crypto_kpp *tfm_ecdh;
|
||||
|
||||
if (cid == L2CAP_CID_SMP_BREDR) {
|
||||
@@ -3298,30 +3264,20 @@ static struct l2cap_chan *smp_add_cid(struct hci_dev *hdev, u16 cid)
|
||||
if (!smp)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
tfm_cmac = crypto_alloc_shash("cmac(aes)", 0, 0);
|
||||
if (IS_ERR(tfm_cmac)) {
|
||||
bt_dev_err(hdev, "Unable to create CMAC crypto context");
|
||||
kfree_sensitive(smp);
|
||||
return ERR_CAST(tfm_cmac);
|
||||
}
|
||||
|
||||
tfm_ecdh = crypto_alloc_kpp("ecdh-nist-p256", 0, 0);
|
||||
if (IS_ERR(tfm_ecdh)) {
|
||||
bt_dev_err(hdev, "Unable to create ECDH crypto context");
|
||||
crypto_free_shash(tfm_cmac);
|
||||
kfree_sensitive(smp);
|
||||
return ERR_CAST(tfm_ecdh);
|
||||
}
|
||||
|
||||
smp->local_oob = false;
|
||||
smp->tfm_cmac = tfm_cmac;
|
||||
smp->tfm_ecdh = tfm_ecdh;
|
||||
|
||||
create_chan:
|
||||
chan = l2cap_chan_create();
|
||||
if (!chan) {
|
||||
if (smp) {
|
||||
crypto_free_shash(smp->tfm_cmac);
|
||||
crypto_free_kpp(smp->tfm_ecdh);
|
||||
kfree_sensitive(smp);
|
||||
}
|
||||
@@ -3368,7 +3324,6 @@ static void smp_del_chan(struct l2cap_chan *chan)
|
||||
smp = chan->data;
|
||||
if (smp) {
|
||||
chan->data = NULL;
|
||||
crypto_free_shash(smp->tfm_cmac);
|
||||
crypto_free_kpp(smp->tfm_ecdh);
|
||||
kfree_sensitive(smp);
|
||||
}
|
||||
@@ -3565,7 +3520,7 @@ static int __init test_s1(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init test_f4(struct crypto_shash *tfm_cmac)
|
||||
static int __init test_f4(void)
|
||||
{
|
||||
const u8 u[32] = {
|
||||
0xe6, 0x9d, 0x35, 0x0e, 0x48, 0x01, 0x03, 0xcc,
|
||||
@@ -3587,7 +3542,7 @@ static int __init test_f4(struct crypto_shash *tfm_cmac)
|
||||
u8 res[16];
|
||||
int err;
|
||||
|
||||
err = smp_f4(tfm_cmac, u, v, x, z, res);
|
||||
err = smp_f4(u, v, x, z, res);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -3597,7 +3552,7 @@ static int __init test_f4(struct crypto_shash *tfm_cmac)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init test_f5(struct crypto_shash *tfm_cmac)
|
||||
static int __init test_f5(void)
|
||||
{
|
||||
const u8 w[32] = {
|
||||
0x98, 0xa6, 0xbf, 0x73, 0xf3, 0x34, 0x8d, 0x86,
|
||||
@@ -3621,7 +3576,7 @@ static int __init test_f5(struct crypto_shash *tfm_cmac)
|
||||
u8 mackey[16], ltk[16];
|
||||
int err;
|
||||
|
||||
err = smp_f5(tfm_cmac, w, n1, n2, a1, a2, mackey, ltk);
|
||||
err = smp_f5(w, n1, n2, a1, a2, mackey, ltk);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -3634,7 +3589,7 @@ static int __init test_f5(struct crypto_shash *tfm_cmac)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init test_f6(struct crypto_shash *tfm_cmac)
|
||||
static int __init test_f6(void)
|
||||
{
|
||||
const u8 w[16] = {
|
||||
0x20, 0x6e, 0x63, 0xce, 0x20, 0x6a, 0x3f, 0xfd,
|
||||
@@ -3657,7 +3612,7 @@ static int __init test_f6(struct crypto_shash *tfm_cmac)
|
||||
u8 res[16];
|
||||
int err;
|
||||
|
||||
err = smp_f6(tfm_cmac, w, n1, n2, r, io_cap, a1, a2, res);
|
||||
err = smp_f6(w, n1, n2, r, io_cap, a1, a2, res);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -3667,7 +3622,7 @@ static int __init test_f6(struct crypto_shash *tfm_cmac)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init test_g2(struct crypto_shash *tfm_cmac)
|
||||
static int __init test_g2(void)
|
||||
{
|
||||
const u8 u[32] = {
|
||||
0xe6, 0x9d, 0x35, 0x0e, 0x48, 0x01, 0x03, 0xcc,
|
||||
@@ -3689,7 +3644,7 @@ static int __init test_g2(struct crypto_shash *tfm_cmac)
|
||||
u32 val;
|
||||
int err;
|
||||
|
||||
err = smp_g2(tfm_cmac, u, v, x, y, &val);
|
||||
err = smp_g2(u, v, x, y, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -3699,7 +3654,7 @@ static int __init test_g2(struct crypto_shash *tfm_cmac)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init test_h6(struct crypto_shash *tfm_cmac)
|
||||
static int __init test_h6(void)
|
||||
{
|
||||
const u8 w[16] = {
|
||||
0x9b, 0x7d, 0x39, 0x0a, 0xa6, 0x10, 0x10, 0x34,
|
||||
@@ -3711,7 +3666,7 @@ static int __init test_h6(struct crypto_shash *tfm_cmac)
|
||||
u8 res[16];
|
||||
int err;
|
||||
|
||||
err = smp_h6(tfm_cmac, w, key_id, res);
|
||||
err = smp_h6(w, key_id, res);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -3736,8 +3691,7 @@ static const struct file_operations test_smp_fops = {
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static int __init run_selftests(struct crypto_shash *tfm_cmac,
|
||||
struct crypto_kpp *tfm_ecdh)
|
||||
static int __init run_selftests(struct crypto_kpp *tfm_ecdh)
|
||||
{
|
||||
ktime_t calltime, delta, rettime;
|
||||
unsigned long long duration;
|
||||
@@ -3769,31 +3723,31 @@ static int __init run_selftests(struct crypto_shash *tfm_cmac,
|
||||
goto done;
|
||||
}
|
||||
|
||||
err = test_f4(tfm_cmac);
|
||||
err = test_f4();
|
||||
if (err) {
|
||||
BT_ERR("smp_f4 test failed");
|
||||
goto done;
|
||||
}
|
||||
|
||||
err = test_f5(tfm_cmac);
|
||||
err = test_f5();
|
||||
if (err) {
|
||||
BT_ERR("smp_f5 test failed");
|
||||
goto done;
|
||||
}
|
||||
|
||||
err = test_f6(tfm_cmac);
|
||||
err = test_f6();
|
||||
if (err) {
|
||||
BT_ERR("smp_f6 test failed");
|
||||
goto done;
|
||||
}
|
||||
|
||||
err = test_g2(tfm_cmac);
|
||||
err = test_g2();
|
||||
if (err) {
|
||||
BT_ERR("smp_g2 test failed");
|
||||
goto done;
|
||||
}
|
||||
|
||||
err = test_h6(tfm_cmac);
|
||||
err = test_h6();
|
||||
if (err) {
|
||||
BT_ERR("smp_h6 test failed");
|
||||
goto done;
|
||||
@@ -3820,26 +3774,17 @@ done:
|
||||
|
||||
int __init bt_selftest_smp(void)
|
||||
{
|
||||
struct crypto_shash *tfm_cmac;
|
||||
struct crypto_kpp *tfm_ecdh;
|
||||
int err;
|
||||
|
||||
tfm_cmac = crypto_alloc_shash("cmac(aes)", 0, 0);
|
||||
if (IS_ERR(tfm_cmac)) {
|
||||
BT_ERR("Unable to create CMAC crypto context");
|
||||
return PTR_ERR(tfm_cmac);
|
||||
}
|
||||
|
||||
tfm_ecdh = crypto_alloc_kpp("ecdh-nist-p256", 0, 0);
|
||||
if (IS_ERR(tfm_ecdh)) {
|
||||
BT_ERR("Unable to create ECDH crypto context");
|
||||
crypto_free_shash(tfm_cmac);
|
||||
return PTR_ERR(tfm_ecdh);
|
||||
}
|
||||
|
||||
err = run_selftests(tfm_cmac, tfm_ecdh);
|
||||
err = run_selftests(tfm_ecdh);
|
||||
|
||||
crypto_free_shash(tfm_cmac);
|
||||
crypto_free_kpp(tfm_ecdh);
|
||||
|
||||
return err;
|
||||
|
||||
+1
-4
@@ -1,11 +1,8 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
BlueZ - Bluetooth protocol stack for Linux
|
||||
Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
|
||||
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.
|
||||
|
||||
Reference in new issue
Block a user