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:
Jakub Kicinski committed 2026-06-11 17:08:04 -07:00
commit 9bbdc7dc40
69 files changed
+955 -2810

No files matched your search

-3
View File
@@ -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
-3
View File
@@ -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
-3
View File
@@ -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
-3
View File
@@ -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
+1 -41
View File
@@ -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
-3
View File
@@ -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
-908
View File
@@ -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);
-749
View File
@@ -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);
+6 -4
View File
@@ -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)
+9
View File
@@ -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
+451 -51
View File
@@ -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);
+25 -1
View File
@@ -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,
+37 -2
View File
@@ -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>");
+7
View File
@@ -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
+1 -1
View File
@@ -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)
+2
View File
@@ -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 -11
View File
@@ -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>
+13
View File
@@ -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
View File
@@ -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);
-614
View File
@@ -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);
+1
View File
@@ -273,6 +273,7 @@ static int h5_close(struct hci_uart *hu)
if (!hu->serdev)
kfree(h5);
hu->priv = NULL;
return 0;
}
+7 -4
View File
@@ -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);
+10
View File
@@ -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 -4
View File
@@ -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.
+7 -4
View File
@@ -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 -4
View File
@@ -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 -4
View File
@@ -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 -4
View File
@@ -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.
+2 -4
View File
@@ -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 -4
View File
@@ -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 -4
View File
@@ -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 -4
View File
@@ -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
View File
@@ -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);
}
}
+1 -5
View File
@@ -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 -4
View File
@@ -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 -4
View File
@@ -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 -4
View File
@@ -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 -4
View File
@@ -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 -4
View File
@@ -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 -4
View File
@@ -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
View File
@@ -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;
+1 -1
View File
@@ -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 -4
View File
@@ -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.
+3 -4
View File
@@ -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 -4
View File
@@ -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 -4
View File
@@ -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.
+8 -4
View File
@@ -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
View File
@@ -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,
+23 -1
View File
@@ -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 -4
View File
@@ -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 -4
View File
@@ -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 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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 -4
View File
@@ -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 -4
View File
@@ -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 -4
View File
@@ -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 -4
View File
@@ -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
View File
@@ -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 -4
View File
@@ -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
View File
@@ -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 -4
View File
@@ -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 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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.