Merge branch 'for-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next
Johan Hedberg says: ==================== pull request: bluetooth-next 2017-10-19 Here's the first bluetooth-next pull request targeting the 4.15 kernel release. - Multiple fixes & improvements to the hci_bcm driver - DT improvements, e.g. new local-bd-address property - Fixes & improvements to ECDH usage. Private key is now generated by the crypto subsystem. - gcc-4.9 warning fixes Please let me know if there are any issues pulling. Thanks. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
f730cc9fee
|
@ -0,0 +1,5 @@
|
|||
The following properties are common to the Bluetooth controllers:
|
||||
|
||||
- local-bd-address: array of 6 bytes, specifies the BD address that was
|
||||
uniquely assigned to the Bluetooth device, formatted with least significant
|
||||
byte first (little-endian).
|
|
@ -37,6 +37,11 @@ The following properties are defined to the bluetooth node:
|
|||
Definition: must be:
|
||||
"qcom,wcnss-bt"
|
||||
|
||||
- local-bd-address:
|
||||
Usage: optional
|
||||
Value type: <u8 array>
|
||||
Definition: see Documentation/devicetree/bindings/net/bluetooth.txt
|
||||
|
||||
== WiFi
|
||||
The following properties are defined to the WiFi node:
|
||||
|
||||
|
@ -91,6 +96,9 @@ smd {
|
|||
|
||||
bt {
|
||||
compatible = "qcom,wcnss-bt";
|
||||
|
||||
/* BD address 00:11:22:33:44:55 */
|
||||
local-bd-address = [ 55 44 33 22 11 00 ];
|
||||
};
|
||||
|
||||
wlan {
|
||||
|
|
|
@ -65,6 +65,7 @@ config BT_HCIBTSDIO
|
|||
|
||||
config BT_HCIUART
|
||||
tristate "HCI UART driver"
|
||||
depends on SERIAL_DEV_BUS || !SERIAL_DEV_BUS
|
||||
depends on TTY
|
||||
help
|
||||
Bluetooth HCI UART driver.
|
||||
|
@ -79,7 +80,6 @@ config BT_HCIUART
|
|||
config BT_HCIUART_SERDEV
|
||||
bool
|
||||
depends on SERIAL_DEV_BUS && BT_HCIUART
|
||||
depends on SERIAL_DEV_BUS=y || SERIAL_DEV_BUS=BT_HCIUART
|
||||
default y
|
||||
|
||||
config BT_HCIUART_H4
|
||||
|
@ -169,6 +169,7 @@ config BT_HCIUART_BCM
|
|||
bool "Broadcom protocol support"
|
||||
depends on BT_HCIUART
|
||||
depends on BT_HCIUART_SERDEV
|
||||
depends on (!ACPI || SERIAL_DEV_CTRL_TTYPORT)
|
||||
select BT_HCIUART_H4
|
||||
select BT_BCM
|
||||
help
|
||||
|
|
|
@ -121,7 +121,7 @@ static void bcm203x_complete(struct urb *urb)
|
|||
}
|
||||
|
||||
data->state = BCM203X_LOAD_FIRMWARE;
|
||||
|
||||
/* fall through */
|
||||
case BCM203X_LOAD_FIRMWARE:
|
||||
if (data->fw_sent == data->fw_size) {
|
||||
usb_fill_int_urb(urb, udev, usb_rcvintpipe(udev, BCM203X_IN_EP),
|
||||
|
|
|
@ -156,9 +156,9 @@ static void bluecard_detach(struct pcmcia_device *p_dev);
|
|||
/* ======================== LED handling routines ======================== */
|
||||
|
||||
|
||||
static void bluecard_activity_led_timeout(u_long arg)
|
||||
static void bluecard_activity_led_timeout(struct timer_list *t)
|
||||
{
|
||||
struct bluecard_info *info = (struct bluecard_info *)arg;
|
||||
struct bluecard_info *info = from_timer(info, t, timer);
|
||||
unsigned int iobase = info->p_dev->resource[0]->start;
|
||||
|
||||
if (test_bit(CARD_ACTIVITY, &(info->hw_state))) {
|
||||
|
@ -691,8 +691,7 @@ static int bluecard_open(struct bluecard_info *info)
|
|||
|
||||
spin_lock_init(&(info->lock));
|
||||
|
||||
setup_timer(&(info->timer), &bluecard_activity_led_timeout,
|
||||
(u_long)info);
|
||||
timer_setup(&info->timer, bluecard_activity_led_timeout, 0);
|
||||
|
||||
skb_queue_head_init(&(info->txq));
|
||||
|
||||
|
|
|
@ -327,6 +327,8 @@ static const struct {
|
|||
{ 0x4406, "BCM4324B3" }, /* 002.004.006 */
|
||||
{ 0x610c, "BCM4354" }, /* 003.001.012 */
|
||||
{ 0x2209, "BCM43430A1" }, /* 001.002.009 */
|
||||
{ 0x6119, "BCM4345C0" }, /* 003.001.025 */
|
||||
{ 0x230f, "BCM4356A2" }, /* 001.003.015 */
|
||||
{ }
|
||||
};
|
||||
|
||||
|
@ -361,6 +363,7 @@ int btbcm_initialize(struct hci_dev *hdev, char *fw_name, size_t len)
|
|||
switch ((rev & 0xf000) >> 12) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
for (i = 0; bcm_uart_subver_table[i].name; i++) {
|
||||
if (subver == bcm_uart_subver_table[i].subver) {
|
||||
|
|
|
@ -64,7 +64,7 @@ static irqreturn_t btmrvl_wake_irq_bt(int irq, void *priv)
|
|||
struct btmrvl_sdio_card *card = priv;
|
||||
struct btmrvl_plt_wake_cfg *cfg = card->plt_wake_cfg;
|
||||
|
||||
pr_info("%s: wake by bt", __func__);
|
||||
pr_info("%s: wake by bt\n", __func__);
|
||||
cfg->wake_by_bt = true;
|
||||
disable_irq_nosync(irq);
|
||||
|
||||
|
@ -87,7 +87,7 @@ static int btmrvl_sdio_probe_of(struct device *dev,
|
|||
|
||||
if (!dev->of_node ||
|
||||
!of_match_node(btmrvl_sdio_of_match_table, dev->of_node)) {
|
||||
pr_err("sdio platform data not available");
|
||||
pr_err("sdio platform data not available\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -99,7 +99,7 @@ static int btmrvl_sdio_probe_of(struct device *dev,
|
|||
if (cfg && card->plt_of_node) {
|
||||
cfg->irq_bt = irq_of_parse_and_map(card->plt_of_node, 0);
|
||||
if (!cfg->irq_bt) {
|
||||
dev_err(dev, "fail to parse irq_bt from device tree");
|
||||
dev_err(dev, "fail to parse irq_bt from device tree\n");
|
||||
cfg->irq_bt = -1;
|
||||
} else {
|
||||
ret = devm_request_irq(dev, cfg->irq_bt,
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/rpmsg.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <linux/soc/qcom/wcnss_ctrl.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
|
@ -26,6 +28,7 @@
|
|||
struct btqcomsmd {
|
||||
struct hci_dev *hdev;
|
||||
|
||||
bdaddr_t bdaddr;
|
||||
struct rpmsg_endpoint *acl_channel;
|
||||
struct rpmsg_endpoint *cmd_channel;
|
||||
};
|
||||
|
@ -100,6 +103,38 @@ static int btqcomsmd_close(struct hci_dev *hdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int btqcomsmd_setup(struct hci_dev *hdev)
|
||||
{
|
||||
struct btqcomsmd *btq = hci_get_drvdata(hdev);
|
||||
struct sk_buff *skb;
|
||||
int err;
|
||||
|
||||
skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb))
|
||||
return PTR_ERR(skb);
|
||||
kfree_skb(skb);
|
||||
|
||||
/* Devices do not have persistent storage for BD address. If no
|
||||
* BD address has been retrieved during probe, mark the device
|
||||
* as having an invalid BD address.
|
||||
*/
|
||||
if (!bacmp(&btq->bdaddr, BDADDR_ANY)) {
|
||||
set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* When setting a configured BD address fails, mark the device
|
||||
* as having an invalid BD address.
|
||||
*/
|
||||
err = qca_set_bdaddr_rome(hdev, &btq->bdaddr);
|
||||
if (err) {
|
||||
set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btqcomsmd_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct btqcomsmd *btq;
|
||||
|
@ -123,6 +158,15 @@ static int btqcomsmd_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(btq->cmd_channel))
|
||||
return PTR_ERR(btq->cmd_channel);
|
||||
|
||||
/* The local-bd-address property is usually injected by the
|
||||
* bootloader which has access to the allocated BD address.
|
||||
*/
|
||||
if (!of_property_read_u8_array(pdev->dev.of_node, "local-bd-address",
|
||||
(u8 *)&btq->bdaddr, sizeof(bdaddr_t))) {
|
||||
dev_info(&pdev->dev, "BD address %pMR retrieved from device-tree",
|
||||
&btq->bdaddr);
|
||||
}
|
||||
|
||||
hdev = hci_alloc_dev();
|
||||
if (!hdev)
|
||||
return -ENOMEM;
|
||||
|
@ -135,6 +179,7 @@ static int btqcomsmd_probe(struct platform_device *pdev)
|
|||
hdev->open = btqcomsmd_open;
|
||||
hdev->close = btqcomsmd_close;
|
||||
hdev->send = btqcomsmd_send;
|
||||
hdev->setup = btqcomsmd_setup;
|
||||
hdev->set_bdaddr = qca_set_bdaddr_rome;
|
||||
|
||||
ret = hci_register_dev(hdev);
|
||||
|
|
|
@ -66,7 +66,6 @@ static struct usb_driver btusb_driver;
|
|||
#define BTUSB_BCM2045 0x40000
|
||||
#define BTUSB_IFNUM_2 0x80000
|
||||
#define BTUSB_CW6622 0x100000
|
||||
#define BTUSB_BCM_NO_PRODID 0x200000
|
||||
|
||||
static const struct usb_device_id btusb_table[] = {
|
||||
/* Generic Bluetooth USB device */
|
||||
|
@ -171,10 +170,6 @@ static const struct usb_device_id btusb_table[] = {
|
|||
{ USB_VENDOR_AND_INTERFACE_INFO(0x0930, 0xff, 0x01, 0x01),
|
||||
.driver_info = BTUSB_BCM_PATCHRAM },
|
||||
|
||||
/* Broadcom devices with missing product id */
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(0x0000, 0x0000, 0xff, 0x01, 0x01),
|
||||
.driver_info = BTUSB_BCM_PATCHRAM | BTUSB_BCM_NO_PRODID },
|
||||
|
||||
/* Intel Bluetooth USB Bootloader (RAM module) */
|
||||
{ USB_DEVICE(0x8087, 0x0a5a),
|
||||
.driver_info = BTUSB_INTEL_BOOT | BTUSB_BROKEN_ISOC },
|
||||
|
@ -2909,19 +2904,6 @@ static int btusb_probe(struct usb_interface *intf,
|
|||
if (id->driver_info == BTUSB_IGNORE)
|
||||
return -ENODEV;
|
||||
|
||||
if (id->driver_info & BTUSB_BCM_NO_PRODID) {
|
||||
struct usb_device *udev = interface_to_usbdev(intf);
|
||||
|
||||
/* For the broken Broadcom devices that show 0000:0000
|
||||
* as USB vendor and product information, check that the
|
||||
* manufacturer string identifies them as Broadcom based
|
||||
* devices.
|
||||
*/
|
||||
if (!udev->manufacturer ||
|
||||
strcmp(udev->manufacturer, "Broadcom Corp"))
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (id->driver_info & BTUSB_ATH3012) {
|
||||
struct usb_device *udev = interface_to_usbdev(intf);
|
||||
|
||||
|
|
|
@ -52,11 +52,13 @@
|
|||
|
||||
#define BCM_AUTOSUSPEND_DELAY 5000 /* default autosleep delay */
|
||||
|
||||
/* platform device driver resources */
|
||||
/* device driver resources */
|
||||
struct bcm_device {
|
||||
/* Must be the first member, hci_serdev.c expects this. */
|
||||
struct hci_uart serdev_hu;
|
||||
struct list_head list;
|
||||
|
||||
struct platform_device *pdev;
|
||||
struct device *dev;
|
||||
|
||||
const char *name;
|
||||
struct gpio_desc *device_wakeup;
|
||||
|
@ -68,7 +70,7 @@ struct bcm_device {
|
|||
u32 init_speed;
|
||||
u32 oper_speed;
|
||||
int irq;
|
||||
u8 irq_polarity;
|
||||
bool irq_active_low;
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
struct hci_uart *hu;
|
||||
|
@ -76,11 +78,6 @@ struct bcm_device {
|
|||
#endif
|
||||
};
|
||||
|
||||
/* serdev driver resources */
|
||||
struct bcm_serdev {
|
||||
struct hci_uart hu;
|
||||
};
|
||||
|
||||
/* generic bcm uart resources */
|
||||
struct bcm_data {
|
||||
struct sk_buff *rx_skb;
|
||||
|
@ -155,6 +152,12 @@ static bool bcm_device_exists(struct bcm_device *device)
|
|||
{
|
||||
struct list_head *p;
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
/* Devices using serdev always exist */
|
||||
if (device && device->hu && device->hu->serdev)
|
||||
return true;
|
||||
#endif
|
||||
|
||||
list_for_each(p, &bcm_device_list) {
|
||||
struct bcm_device *dev = list_entry(p, struct bcm_device, list);
|
||||
|
||||
|
@ -188,9 +191,9 @@ static irqreturn_t bcm_host_wake(int irq, void *data)
|
|||
|
||||
bt_dev_dbg(bdev, "Host wake IRQ");
|
||||
|
||||
pm_runtime_get(&bdev->pdev->dev);
|
||||
pm_runtime_mark_last_busy(&bdev->pdev->dev);
|
||||
pm_runtime_put_autosuspend(&bdev->pdev->dev);
|
||||
pm_runtime_get(bdev->dev);
|
||||
pm_runtime_mark_last_busy(bdev->dev);
|
||||
pm_runtime_put_autosuspend(bdev->dev);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
@ -200,7 +203,6 @@ static int bcm_request_irq(struct bcm_data *bcm)
|
|||
struct bcm_device *bdev = bcm->dev;
|
||||
int err;
|
||||
|
||||
/* If this is not a platform device, do not enable PM functionalities */
|
||||
mutex_lock(&bcm_device_lock);
|
||||
if (!bcm_device_exists(bdev)) {
|
||||
err = -ENODEV;
|
||||
|
@ -212,18 +214,20 @@ static int bcm_request_irq(struct bcm_data *bcm)
|
|||
goto unlock;
|
||||
}
|
||||
|
||||
err = devm_request_irq(&bdev->pdev->dev, bdev->irq, bcm_host_wake,
|
||||
IRQF_TRIGGER_RISING, "host_wake", bdev);
|
||||
err = devm_request_irq(bdev->dev, bdev->irq, bcm_host_wake,
|
||||
bdev->irq_active_low ? IRQF_TRIGGER_FALLING :
|
||||
IRQF_TRIGGER_RISING,
|
||||
"host_wake", bdev);
|
||||
if (err)
|
||||
goto unlock;
|
||||
|
||||
device_init_wakeup(&bdev->pdev->dev, true);
|
||||
device_init_wakeup(bdev->dev, true);
|
||||
|
||||
pm_runtime_set_autosuspend_delay(&bdev->pdev->dev,
|
||||
pm_runtime_set_autosuspend_delay(bdev->dev,
|
||||
BCM_AUTOSUSPEND_DELAY);
|
||||
pm_runtime_use_autosuspend(&bdev->pdev->dev);
|
||||
pm_runtime_set_active(&bdev->pdev->dev);
|
||||
pm_runtime_enable(&bdev->pdev->dev);
|
||||
pm_runtime_use_autosuspend(bdev->dev);
|
||||
pm_runtime_set_active(bdev->dev);
|
||||
pm_runtime_enable(bdev->dev);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&bcm_device_lock);
|
||||
|
@ -253,7 +257,7 @@ static int bcm_setup_sleep(struct hci_uart *hu)
|
|||
struct sk_buff *skb;
|
||||
struct bcm_set_sleep_mode sleep_params = default_sleep_params;
|
||||
|
||||
sleep_params.host_wake_active = !bcm->dev->irq_polarity;
|
||||
sleep_params.host_wake_active = !bcm->dev->irq_active_low;
|
||||
|
||||
skb = __hci_cmd_sync(hu->hdev, 0xfc27, sizeof(sleep_params),
|
||||
&sleep_params, HCI_INIT_TIMEOUT);
|
||||
|
@ -311,18 +315,17 @@ static int bcm_open(struct hci_uart *hu)
|
|||
|
||||
hu->priv = bcm;
|
||||
|
||||
/* If this is a serdev defined device, then only use
|
||||
* serdev open primitive and skip the rest.
|
||||
*/
|
||||
mutex_lock(&bcm_device_lock);
|
||||
|
||||
if (hu->serdev) {
|
||||
serdev_device_open(hu->serdev);
|
||||
bcm->dev = serdev_device_get_drvdata(hu->serdev);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!hu->tty->dev)
|
||||
goto out;
|
||||
|
||||
mutex_lock(&bcm_device_lock);
|
||||
list_for_each(p, &bcm_device_list) {
|
||||
struct bcm_device *dev = list_entry(p, struct bcm_device, list);
|
||||
|
||||
|
@ -330,50 +333,56 @@ static int bcm_open(struct hci_uart *hu)
|
|||
* platform device (saved during device probe) and
|
||||
* parent of tty device used by hci_uart
|
||||
*/
|
||||
if (hu->tty->dev->parent == dev->pdev->dev.parent) {
|
||||
if (hu->tty->dev->parent == dev->dev->parent) {
|
||||
bcm->dev = dev;
|
||||
hu->init_speed = dev->init_speed;
|
||||
hu->oper_speed = dev->oper_speed;
|
||||
#ifdef CONFIG_PM
|
||||
dev->hu = hu;
|
||||
#endif
|
||||
bcm_gpio_set_power(bcm->dev, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&bcm_device_lock);
|
||||
out:
|
||||
if (bcm->dev) {
|
||||
hu->init_speed = bcm->dev->init_speed;
|
||||
hu->oper_speed = bcm->dev->oper_speed;
|
||||
bcm_gpio_set_power(bcm->dev, true);
|
||||
}
|
||||
|
||||
mutex_unlock(&bcm_device_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcm_close(struct hci_uart *hu)
|
||||
{
|
||||
struct bcm_data *bcm = hu->priv;
|
||||
struct bcm_device *bdev = bcm->dev;
|
||||
struct bcm_device *bdev = NULL;
|
||||
|
||||
bt_dev_dbg(hu->hdev, "hu %p", hu);
|
||||
|
||||
/* If this is a serdev defined device, only use serdev
|
||||
* close primitive and then continue as usual.
|
||||
*/
|
||||
if (hu->serdev)
|
||||
serdev_device_close(hu->serdev);
|
||||
|
||||
/* Protect bcm->dev against removal of the device or driver */
|
||||
mutex_lock(&bcm_device_lock);
|
||||
if (bcm_device_exists(bdev)) {
|
||||
|
||||
if (hu->serdev) {
|
||||
serdev_device_close(hu->serdev);
|
||||
bdev = serdev_device_get_drvdata(hu->serdev);
|
||||
} else if (bcm_device_exists(bcm->dev)) {
|
||||
bdev = bcm->dev;
|
||||
#ifdef CONFIG_PM
|
||||
bdev->hu = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (bdev) {
|
||||
bcm_gpio_set_power(bdev, false);
|
||||
#ifdef CONFIG_PM
|
||||
pm_runtime_disable(&bdev->pdev->dev);
|
||||
pm_runtime_set_suspended(&bdev->pdev->dev);
|
||||
pm_runtime_disable(bdev->dev);
|
||||
pm_runtime_set_suspended(bdev->dev);
|
||||
|
||||
if (device_can_wakeup(&bdev->pdev->dev)) {
|
||||
devm_free_irq(&bdev->pdev->dev, bdev->irq, bdev);
|
||||
device_init_wakeup(&bdev->pdev->dev, false);
|
||||
if (device_can_wakeup(bdev->dev)) {
|
||||
devm_free_irq(bdev->dev, bdev->irq, bdev);
|
||||
device_init_wakeup(bdev->dev, false);
|
||||
}
|
||||
|
||||
bdev->hu = NULL;
|
||||
#endif
|
||||
}
|
||||
mutex_unlock(&bcm_device_lock);
|
||||
|
@ -504,9 +513,9 @@ static int bcm_recv(struct hci_uart *hu, const void *data, int count)
|
|||
/* Delay auto-suspend when receiving completed packet */
|
||||
mutex_lock(&bcm_device_lock);
|
||||
if (bcm->dev && bcm_device_exists(bcm->dev)) {
|
||||
pm_runtime_get(&bcm->dev->pdev->dev);
|
||||
pm_runtime_mark_last_busy(&bcm->dev->pdev->dev);
|
||||
pm_runtime_put_autosuspend(&bcm->dev->pdev->dev);
|
||||
pm_runtime_get(bcm->dev->dev);
|
||||
pm_runtime_mark_last_busy(bcm->dev->dev);
|
||||
pm_runtime_put_autosuspend(bcm->dev->dev);
|
||||
}
|
||||
mutex_unlock(&bcm_device_lock);
|
||||
}
|
||||
|
@ -537,15 +546,15 @@ static struct sk_buff *bcm_dequeue(struct hci_uart *hu)
|
|||
|
||||
if (bcm_device_exists(bcm->dev)) {
|
||||
bdev = bcm->dev;
|
||||
pm_runtime_get_sync(&bdev->pdev->dev);
|
||||
pm_runtime_get_sync(bdev->dev);
|
||||
/* Shall be resumed here */
|
||||
}
|
||||
|
||||
skb = skb_dequeue(&bcm->txq);
|
||||
|
||||
if (bdev) {
|
||||
pm_runtime_mark_last_busy(&bdev->pdev->dev);
|
||||
pm_runtime_put_autosuspend(&bdev->pdev->dev);
|
||||
pm_runtime_mark_last_busy(bdev->dev);
|
||||
pm_runtime_put_autosuspend(bdev->dev);
|
||||
}
|
||||
|
||||
mutex_unlock(&bcm_device_lock);
|
||||
|
@ -556,7 +565,7 @@ static struct sk_buff *bcm_dequeue(struct hci_uart *hu)
|
|||
#ifdef CONFIG_PM
|
||||
static int bcm_suspend_device(struct device *dev)
|
||||
{
|
||||
struct bcm_device *bdev = platform_get_drvdata(to_platform_device(dev));
|
||||
struct bcm_device *bdev = dev_get_drvdata(dev);
|
||||
|
||||
bt_dev_dbg(bdev, "");
|
||||
|
||||
|
@ -579,7 +588,7 @@ static int bcm_suspend_device(struct device *dev)
|
|||
|
||||
static int bcm_resume_device(struct device *dev)
|
||||
{
|
||||
struct bcm_device *bdev = platform_get_drvdata(to_platform_device(dev));
|
||||
struct bcm_device *bdev = dev_get_drvdata(dev);
|
||||
|
||||
bt_dev_dbg(bdev, "");
|
||||
|
||||
|
@ -601,16 +610,18 @@ static int bcm_resume_device(struct device *dev)
|
|||
#endif
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
/* Platform suspend callback */
|
||||
/* suspend callback */
|
||||
static int bcm_suspend(struct device *dev)
|
||||
{
|
||||
struct bcm_device *bdev = platform_get_drvdata(to_platform_device(dev));
|
||||
struct bcm_device *bdev = dev_get_drvdata(dev);
|
||||
int error;
|
||||
|
||||
bt_dev_dbg(bdev, "suspend: is_suspended %d", bdev->is_suspended);
|
||||
|
||||
/* bcm_suspend can be called at any time as long as platform device is
|
||||
* bound, so it should use bcm_device_lock to protect access to hci_uart
|
||||
/*
|
||||
* When used with a device instantiated as platform_device, bcm_suspend
|
||||
* can be called at any time as long as the platform device is bound,
|
||||
* so it should use bcm_device_lock to protect access to hci_uart
|
||||
* and device_wake-up GPIO.
|
||||
*/
|
||||
mutex_lock(&bcm_device_lock);
|
||||
|
@ -621,7 +632,7 @@ static int bcm_suspend(struct device *dev)
|
|||
if (pm_runtime_active(dev))
|
||||
bcm_suspend_device(dev);
|
||||
|
||||
if (device_may_wakeup(&bdev->pdev->dev)) {
|
||||
if (device_may_wakeup(dev)) {
|
||||
error = enable_irq_wake(bdev->irq);
|
||||
if (!error)
|
||||
bt_dev_dbg(bdev, "BCM irq: enabled");
|
||||
|
@ -633,15 +644,17 @@ static int bcm_suspend(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Platform resume callback */
|
||||
/* resume callback */
|
||||
static int bcm_resume(struct device *dev)
|
||||
{
|
||||
struct bcm_device *bdev = platform_get_drvdata(to_platform_device(dev));
|
||||
struct bcm_device *bdev = dev_get_drvdata(dev);
|
||||
|
||||
bt_dev_dbg(bdev, "resume: is_suspended %d", bdev->is_suspended);
|
||||
|
||||
/* bcm_resume can be called at any time as long as platform device is
|
||||
* bound, so it should use bcm_device_lock to protect access to hci_uart
|
||||
/*
|
||||
* When used with a device instantiated as platform_device, bcm_resume
|
||||
* can be called at any time as long as platform device is bound,
|
||||
* so it should use bcm_device_lock to protect access to hci_uart
|
||||
* and device_wake-up GPIO.
|
||||
*/
|
||||
mutex_lock(&bcm_device_lock);
|
||||
|
@ -649,7 +662,7 @@ static int bcm_resume(struct device *dev)
|
|||
if (!bdev->hu)
|
||||
goto unlock;
|
||||
|
||||
if (device_may_wakeup(&bdev->pdev->dev)) {
|
||||
if (device_may_wakeup(dev)) {
|
||||
disable_irq_wake(bdev->irq);
|
||||
bt_dev_dbg(bdev, "BCM irq: disabled");
|
||||
}
|
||||
|
@ -690,10 +703,8 @@ static const struct acpi_gpio_mapping acpi_bcm_int_first_gpios[] = {
|
|||
};
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static u8 acpi_active_low = ACPI_ACTIVE_LOW;
|
||||
|
||||
/* IRQ polarity of some chipsets are not defined correctly in ACPI table. */
|
||||
static const struct dmi_system_id bcm_wrong_irq_dmi_table[] = {
|
||||
static const struct dmi_system_id bcm_active_low_irq_dmi_table[] = {
|
||||
{
|
||||
.ident = "Asus T100TA",
|
||||
.matches = {
|
||||
|
@ -701,7 +712,6 @@ static const struct dmi_system_id bcm_wrong_irq_dmi_table[] = {
|
|||
"ASUSTeK COMPUTER INC."),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100TA"),
|
||||
},
|
||||
.driver_data = &acpi_active_low,
|
||||
},
|
||||
{
|
||||
.ident = "Asus T100CHI",
|
||||
|
@ -710,7 +720,6 @@ static const struct dmi_system_id bcm_wrong_irq_dmi_table[] = {
|
|||
"ASUSTeK COMPUTER INC."),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100CHI"),
|
||||
},
|
||||
.driver_data = &acpi_active_low,
|
||||
},
|
||||
{ /* Handle ThinkPad 8 tablets with BCM2E55 chipset ACPI ID */
|
||||
.ident = "Lenovo ThinkPad 8",
|
||||
|
@ -718,7 +727,13 @@ static const struct dmi_system_id bcm_wrong_irq_dmi_table[] = {
|
|||
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "ThinkPad 8"),
|
||||
},
|
||||
.driver_data = &acpi_active_low,
|
||||
},
|
||||
{
|
||||
.ident = "MINIX Z83-4",
|
||||
.matches = {
|
||||
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "MINIX"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Z83-4"),
|
||||
},
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
@ -733,13 +748,13 @@ static int bcm_resource(struct acpi_resource *ares, void *data)
|
|||
switch (ares->type) {
|
||||
case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
|
||||
irq = &ares->data.extended_irq;
|
||||
dev->irq_polarity = irq->polarity;
|
||||
dev->irq_active_low = irq->polarity == ACPI_ACTIVE_LOW;
|
||||
break;
|
||||
|
||||
case ACPI_RESOURCE_TYPE_GPIO:
|
||||
gpio = &ares->data.gpio;
|
||||
if (gpio->connection_type == ACPI_RESOURCE_GPIO_TYPE_INT)
|
||||
dev->irq_polarity = gpio->polarity;
|
||||
dev->irq_active_low = gpio->polarity == ACPI_ACTIVE_LOW;
|
||||
break;
|
||||
|
||||
case ACPI_RESOURCE_TYPE_SERIAL_BUS:
|
||||
|
@ -754,36 +769,32 @@ static int bcm_resource(struct acpi_resource *ares, void *data)
|
|||
break;
|
||||
}
|
||||
|
||||
/* Always tell the ACPI core to skip this resource */
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_ACPI */
|
||||
|
||||
static int bcm_platform_probe(struct bcm_device *dev)
|
||||
static int bcm_get_resources(struct bcm_device *dev)
|
||||
{
|
||||
struct platform_device *pdev = dev->pdev;
|
||||
dev->name = dev_name(dev->dev);
|
||||
|
||||
dev->name = dev_name(&pdev->dev);
|
||||
dev->clk = devm_clk_get(dev->dev, NULL);
|
||||
|
||||
dev->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
|
||||
dev->device_wakeup = devm_gpiod_get_optional(&pdev->dev,
|
||||
dev->device_wakeup = devm_gpiod_get_optional(dev->dev,
|
||||
"device-wakeup",
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR(dev->device_wakeup))
|
||||
return PTR_ERR(dev->device_wakeup);
|
||||
|
||||
dev->shutdown = devm_gpiod_get_optional(&pdev->dev, "shutdown",
|
||||
dev->shutdown = devm_gpiod_get_optional(dev->dev, "shutdown",
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR(dev->shutdown))
|
||||
return PTR_ERR(dev->shutdown);
|
||||
|
||||
/* IRQ can be declared in ACPI table as Interrupt or GpioInt */
|
||||
dev->irq = platform_get_irq(pdev, 0);
|
||||
if (dev->irq <= 0) {
|
||||
struct gpio_desc *gpio;
|
||||
|
||||
gpio = devm_gpiod_get_optional(&pdev->dev, "host-wakeup",
|
||||
gpio = devm_gpiod_get_optional(dev->dev, "host-wakeup",
|
||||
GPIOD_IN);
|
||||
if (IS_ERR(gpio))
|
||||
return PTR_ERR(gpio);
|
||||
|
@ -791,54 +802,48 @@ static int bcm_platform_probe(struct bcm_device *dev)
|
|||
dev->irq = gpiod_to_irq(gpio);
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "BCM irq: %d\n", dev->irq);
|
||||
|
||||
/* Make sure at-least one of the GPIO is defined and that
|
||||
* a name is specified for this instance
|
||||
*/
|
||||
if ((!dev->device_wakeup && !dev->shutdown) || !dev->name) {
|
||||
dev_err(&pdev->dev, "invalid platform data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_info(dev->dev, "BCM irq: %d\n", dev->irq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static int bcm_acpi_probe(struct bcm_device *dev)
|
||||
{
|
||||
struct platform_device *pdev = dev->pdev;
|
||||
LIST_HEAD(resources);
|
||||
const struct dmi_system_id *dmi_id;
|
||||
const struct acpi_gpio_mapping *gpio_mapping = acpi_bcm_int_last_gpios;
|
||||
const struct acpi_device_id *id;
|
||||
struct resource_entry *entry;
|
||||
int ret;
|
||||
|
||||
/* Retrieve GPIO data */
|
||||
id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev);
|
||||
id = acpi_match_device(dev->dev->driver->acpi_match_table, dev->dev);
|
||||
if (id)
|
||||
gpio_mapping = (const struct acpi_gpio_mapping *) id->driver_data;
|
||||
|
||||
ret = devm_acpi_dev_add_driver_gpios(&pdev->dev, gpio_mapping);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = bcm_platform_probe(dev);
|
||||
ret = devm_acpi_dev_add_driver_gpios(dev->dev, gpio_mapping);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Retrieve UART ACPI info */
|
||||
ret = acpi_dev_get_resources(ACPI_COMPANION(&dev->pdev->dev),
|
||||
ret = acpi_dev_get_resources(ACPI_COMPANION(dev->dev),
|
||||
&resources, bcm_resource, dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
resource_list_for_each_entry(entry, &resources) {
|
||||
if (resource_type(entry->res) == IORESOURCE_IRQ) {
|
||||
dev->irq = entry->res->start;
|
||||
break;
|
||||
}
|
||||
}
|
||||
acpi_dev_free_resource_list(&resources);
|
||||
|
||||
dmi_id = dmi_first_match(bcm_wrong_irq_dmi_table);
|
||||
dmi_id = dmi_first_match(bcm_active_low_irq_dmi_table);
|
||||
if (dmi_id) {
|
||||
bt_dev_warn(dev, "%s: Overwriting IRQ polarity to active low",
|
||||
dev_warn(dev->dev, "%s: Overwriting IRQ polarity to active low",
|
||||
dmi_id->ident);
|
||||
dev->irq_polarity = *(u8 *)dmi_id->driver_data;
|
||||
dev->irq_active_low = true;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -850,6 +855,12 @@ static int bcm_acpi_probe(struct bcm_device *dev)
|
|||
}
|
||||
#endif /* CONFIG_ACPI */
|
||||
|
||||
static int bcm_of_probe(struct bcm_device *bdev)
|
||||
{
|
||||
device_property_read_u32(bdev->dev, "max-speed", &bdev->oper_speed);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcm_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct bcm_device *dev;
|
||||
|
@ -859,12 +870,16 @@ static int bcm_probe(struct platform_device *pdev)
|
|||
if (!dev)
|
||||
return -ENOMEM;
|
||||
|
||||
dev->pdev = pdev;
|
||||
dev->dev = &pdev->dev;
|
||||
dev->irq = platform_get_irq(pdev, 0);
|
||||
|
||||
if (has_acpi_companion(&pdev->dev))
|
||||
if (has_acpi_companion(&pdev->dev)) {
|
||||
ret = bcm_acpi_probe(dev);
|
||||
else
|
||||
ret = bcm_platform_probe(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = bcm_get_resources(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -926,14 +941,16 @@ static const struct acpi_device_id bcm_acpi_match[] = {
|
|||
{ "BCM2E71", (kernel_ulong_t)&acpi_bcm_int_last_gpios },
|
||||
{ "BCM2E7B", (kernel_ulong_t)&acpi_bcm_int_last_gpios },
|
||||
{ "BCM2E7C", (kernel_ulong_t)&acpi_bcm_int_last_gpios },
|
||||
{ "BCM2E7E", (kernel_ulong_t)&acpi_bcm_int_first_gpios },
|
||||
{ "BCM2E95", (kernel_ulong_t)&acpi_bcm_int_first_gpios },
|
||||
{ "BCM2E96", (kernel_ulong_t)&acpi_bcm_int_first_gpios },
|
||||
{ "BCM2EA4", (kernel_ulong_t)&acpi_bcm_int_first_gpios },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, bcm_acpi_match);
|
||||
#endif
|
||||
|
||||
/* Platform suspend and resume callbacks */
|
||||
/* suspend and resume callbacks */
|
||||
static const struct dev_pm_ops bcm_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(bcm_suspend, bcm_resume)
|
||||
SET_RUNTIME_PM_OPS(bcm_suspend_device, bcm_resume_device, NULL)
|
||||
|
@ -951,29 +968,41 @@ static struct platform_driver bcm_driver = {
|
|||
|
||||
static int bcm_serdev_probe(struct serdev_device *serdev)
|
||||
{
|
||||
struct bcm_serdev *bcmdev;
|
||||
u32 speed;
|
||||
struct bcm_device *bcmdev;
|
||||
int err;
|
||||
|
||||
bcmdev = devm_kzalloc(&serdev->dev, sizeof(*bcmdev), GFP_KERNEL);
|
||||
if (!bcmdev)
|
||||
return -ENOMEM;
|
||||
|
||||
bcmdev->hu.serdev = serdev;
|
||||
bcmdev->dev = &serdev->dev;
|
||||
#ifdef CONFIG_PM
|
||||
bcmdev->hu = &bcmdev->serdev_hu;
|
||||
#endif
|
||||
bcmdev->serdev_hu.serdev = serdev;
|
||||
serdev_device_set_drvdata(serdev, bcmdev);
|
||||
|
||||
err = device_property_read_u32(&serdev->dev, "max-speed", &speed);
|
||||
if (!err)
|
||||
bcmdev->hu.oper_speed = speed;
|
||||
if (has_acpi_companion(&serdev->dev))
|
||||
err = bcm_acpi_probe(bcmdev);
|
||||
else
|
||||
err = bcm_of_probe(bcmdev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return hci_uart_register_device(&bcmdev->hu, &bcm_proto);
|
||||
err = bcm_get_resources(bcmdev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
bcm_gpio_set_power(bcmdev, false);
|
||||
|
||||
return hci_uart_register_device(&bcmdev->serdev_hu, &bcm_proto);
|
||||
}
|
||||
|
||||
static void bcm_serdev_remove(struct serdev_device *serdev)
|
||||
{
|
||||
struct bcm_serdev *bcmdev = serdev_device_get_drvdata(serdev);
|
||||
struct bcm_device *bcmdev = serdev_device_get_drvdata(serdev);
|
||||
|
||||
hci_uart_unregister_device(&bcmdev->hu);
|
||||
hci_uart_unregister_device(&bcmdev->serdev_hu);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
|
@ -990,6 +1019,8 @@ static struct serdev_device_driver bcm_serdev_driver = {
|
|||
.driver = {
|
||||
.name = "hci_uart_bcm",
|
||||
.of_match_table = of_match_ptr(bcm_bluetooth_of_match),
|
||||
.acpi_match_table = ACPI_PTR(bcm_acpi_match),
|
||||
.pm = &bcm_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -65,6 +65,7 @@ struct bcsp_struct {
|
|||
u8 rxseq_txack; /* rxseq == txack. */
|
||||
u8 rxack; /* Last packet sent by us that the peer ack'ed */
|
||||
struct timer_list tbcsp;
|
||||
struct hci_uart *hu;
|
||||
|
||||
enum {
|
||||
BCSP_W4_PKT_DELIMITER,
|
||||
|
@ -697,10 +698,10 @@ static int bcsp_recv(struct hci_uart *hu, const void *data, int count)
|
|||
}
|
||||
|
||||
/* Arrange to retransmit all messages in the relq. */
|
||||
static void bcsp_timed_event(unsigned long arg)
|
||||
static void bcsp_timed_event(struct timer_list *t)
|
||||
{
|
||||
struct hci_uart *hu = (struct hci_uart *)arg;
|
||||
struct bcsp_struct *bcsp = hu->priv;
|
||||
struct bcsp_struct *bcsp = from_timer(bcsp, t, tbcsp);
|
||||
struct hci_uart *hu = bcsp->hu;
|
||||
struct sk_buff *skb;
|
||||
unsigned long flags;
|
||||
|
||||
|
@ -729,11 +730,12 @@ static int bcsp_open(struct hci_uart *hu)
|
|||
return -ENOMEM;
|
||||
|
||||
hu->priv = bcsp;
|
||||
bcsp->hu = hu;
|
||||
skb_queue_head_init(&bcsp->unack);
|
||||
skb_queue_head_init(&bcsp->rel);
|
||||
skb_queue_head_init(&bcsp->unrel);
|
||||
|
||||
setup_timer(&bcsp->tbcsp, bcsp_timed_event, (u_long)hu);
|
||||
timer_setup(&bcsp->tbcsp, bcsp_timed_event, 0);
|
||||
|
||||
bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
|
||||
|
||||
|
|
|
@ -78,6 +78,7 @@ struct h5 {
|
|||
int (*rx_func)(struct hci_uart *hu, u8 c);
|
||||
|
||||
struct timer_list timer; /* Retransmission timer */
|
||||
struct hci_uart *hu; /* Parent HCI UART */
|
||||
|
||||
u8 tx_seq; /* Next seq number to send */
|
||||
u8 tx_ack; /* Next ack number to send */
|
||||
|
@ -120,12 +121,12 @@ static u8 h5_cfg_field(struct h5 *h5)
|
|||
return h5->tx_win & 0x07;
|
||||
}
|
||||
|
||||
static void h5_timed_event(unsigned long arg)
|
||||
static void h5_timed_event(struct timer_list *t)
|
||||
{
|
||||
const unsigned char sync_req[] = { 0x01, 0x7e };
|
||||
unsigned char conf_req[3] = { 0x03, 0xfc };
|
||||
struct hci_uart *hu = (struct hci_uart *)arg;
|
||||
struct h5 *h5 = hu->priv;
|
||||
struct h5 *h5 = from_timer(h5, t, timer);
|
||||
struct hci_uart *hu = h5->hu;
|
||||
struct sk_buff *skb;
|
||||
unsigned long flags;
|
||||
|
||||
|
@ -197,6 +198,7 @@ static int h5_open(struct hci_uart *hu)
|
|||
return -ENOMEM;
|
||||
|
||||
hu->priv = h5;
|
||||
h5->hu = hu;
|
||||
|
||||
skb_queue_head_init(&h5->unack);
|
||||
skb_queue_head_init(&h5->rel);
|
||||
|
@ -204,7 +206,7 @@ static int h5_open(struct hci_uart *hu)
|
|||
|
||||
h5_reset_rx(h5);
|
||||
|
||||
setup_timer(&h5->timer, h5_timed_event, (unsigned long)hu);
|
||||
timer_setup(&h5->timer, h5_timed_event, 0);
|
||||
|
||||
h5->tx_win = H5_TX_WIN_MAX;
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include <linux/ioctl.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/serdev.h>
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
|
@ -298,6 +299,12 @@ void hci_uart_set_flow_control(struct hci_uart *hu, bool enable)
|
|||
unsigned int set = 0;
|
||||
unsigned int clear = 0;
|
||||
|
||||
if (hu->serdev) {
|
||||
serdev_device_set_flow_control(hu->serdev, !enable);
|
||||
serdev_device_set_rts(hu->serdev, !enable);
|
||||
return;
|
||||
}
|
||||
|
||||
if (enable) {
|
||||
/* Disable hardware flow control */
|
||||
ktermios = tty->termios;
|
||||
|
|
|
@ -242,7 +242,7 @@ static void ll_device_want_to_wakeup(struct hci_uart *hu)
|
|||
* perfectly safe to always send one.
|
||||
*/
|
||||
BT_DBG("dual wake-up-indication");
|
||||
/* deliberate fall-through - do not add break */
|
||||
/* fall through */
|
||||
case HCILL_ASLEEP:
|
||||
/* acknowledge device wake up */
|
||||
if (send_hcill_cmd(HCILL_WAKE_UP_ACK, hu) < 0) {
|
||||
|
|
|
@ -307,10 +307,10 @@ static void qca_wq_serial_tx_clock_vote_off(struct work_struct *work)
|
|||
serial_clock_vote(HCI_IBS_TX_VOTE_CLOCK_OFF, hu);
|
||||
}
|
||||
|
||||
static void hci_ibs_tx_idle_timeout(unsigned long arg)
|
||||
static void hci_ibs_tx_idle_timeout(struct timer_list *t)
|
||||
{
|
||||
struct hci_uart *hu = (struct hci_uart *)arg;
|
||||
struct qca_data *qca = hu->priv;
|
||||
struct qca_data *qca = from_timer(qca, t, tx_idle_timer);
|
||||
struct hci_uart *hu = qca->hu;
|
||||
unsigned long flags;
|
||||
|
||||
BT_DBG("hu %p idle timeout in %d state", hu, qca->tx_ibs_state);
|
||||
|
@ -342,10 +342,10 @@ static void hci_ibs_tx_idle_timeout(unsigned long arg)
|
|||
spin_unlock_irqrestore(&qca->hci_ibs_lock, flags);
|
||||
}
|
||||
|
||||
static void hci_ibs_wake_retrans_timeout(unsigned long arg)
|
||||
static void hci_ibs_wake_retrans_timeout(struct timer_list *t)
|
||||
{
|
||||
struct hci_uart *hu = (struct hci_uart *)arg;
|
||||
struct qca_data *qca = hu->priv;
|
||||
struct qca_data *qca = from_timer(qca, t, wake_retrans_timer);
|
||||
struct hci_uart *hu = qca->hu;
|
||||
unsigned long flags, retrans_delay;
|
||||
bool retransmit = false;
|
||||
|
||||
|
@ -438,11 +438,10 @@ static int qca_open(struct hci_uart *hu)
|
|||
|
||||
hu->priv = qca;
|
||||
|
||||
setup_timer(&qca->wake_retrans_timer, hci_ibs_wake_retrans_timeout,
|
||||
(u_long)hu);
|
||||
timer_setup(&qca->wake_retrans_timer, hci_ibs_wake_retrans_timeout, 0);
|
||||
qca->wake_retrans = IBS_WAKE_RETRANS_TIMEOUT_MS;
|
||||
|
||||
setup_timer(&qca->tx_idle_timer, hci_ibs_tx_idle_timeout, (u_long)hu);
|
||||
timer_setup(&qca->tx_idle_timer, hci_ibs_tx_idle_timeout, 0);
|
||||
qca->tx_idle_delay = IBS_TX_IDLE_TIMEOUT_MS;
|
||||
|
||||
BT_DBG("HCI_UART_QCA open, tx_idle_delay=%u, wake_retrans=%u",
|
||||
|
|
|
@ -1944,7 +1944,7 @@ static int ca8210_skb_tx(
|
|||
)
|
||||
{
|
||||
int status;
|
||||
struct ieee802154_hdr header = { 0 };
|
||||
struct ieee802154_hdr header = { };
|
||||
struct secspec secspec;
|
||||
unsigned int mac_len;
|
||||
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
#include "ecdh_helper.h"
|
||||
|
||||
#include <linux/scatterlist.h>
|
||||
#include <crypto/kpp.h>
|
||||
#include <crypto/ecdh.h>
|
||||
|
||||
struct ecdh_completion {
|
||||
|
@ -50,55 +49,35 @@ static inline void swap_digits(u64 *in, u64 *out, unsigned int ndigits)
|
|||
out[i] = __swab64(in[ndigits - 1 - i]);
|
||||
}
|
||||
|
||||
bool compute_ecdh_secret(const u8 public_key[64], const u8 private_key[32],
|
||||
u8 secret[32])
|
||||
/* compute_ecdh_secret() - function assumes that the private key was
|
||||
* already set.
|
||||
* @tfm: KPP tfm handle allocated with crypto_alloc_kpp().
|
||||
* @public_key: pair's ecc public key.
|
||||
* secret: memory where the ecdh computed shared secret will be saved.
|
||||
*
|
||||
* Return: zero on success; error code in case of error.
|
||||
*/
|
||||
int compute_ecdh_secret(struct crypto_kpp *tfm, const u8 public_key[64],
|
||||
u8 secret[32])
|
||||
{
|
||||
struct crypto_kpp *tfm;
|
||||
struct kpp_request *req;
|
||||
struct ecdh p;
|
||||
u8 *tmp;
|
||||
struct ecdh_completion result;
|
||||
struct scatterlist src, dst;
|
||||
u8 *tmp, *buf;
|
||||
unsigned int buf_len;
|
||||
int err = -ENOMEM;
|
||||
int err;
|
||||
|
||||
tmp = kmalloc(64, GFP_KERNEL);
|
||||
if (!tmp)
|
||||
return false;
|
||||
return -ENOMEM;
|
||||
|
||||
tfm = crypto_alloc_kpp("ecdh", CRYPTO_ALG_INTERNAL, 0);
|
||||
if (IS_ERR(tfm)) {
|
||||
pr_err("alg: kpp: Failed to load tfm for kpp: %ld\n",
|
||||
PTR_ERR(tfm));
|
||||
req = kpp_request_alloc(tfm, GFP_KERNEL);
|
||||
if (!req) {
|
||||
err = -ENOMEM;
|
||||
goto free_tmp;
|
||||
}
|
||||
|
||||
req = kpp_request_alloc(tfm, GFP_KERNEL);
|
||||
if (!req)
|
||||
goto free_kpp;
|
||||
|
||||
init_completion(&result.completion);
|
||||
|
||||
/* Security Manager Protocol holds digits in litte-endian order
|
||||
* while ECC API expect big-endian data
|
||||
*/
|
||||
swap_digits((u64 *)private_key, (u64 *)tmp, 4);
|
||||
p.key = (char *)tmp;
|
||||
p.key_size = 32;
|
||||
/* Set curve_id */
|
||||
p.curve_id = ECC_CURVE_NIST_P256;
|
||||
buf_len = crypto_ecdh_key_len(&p);
|
||||
buf = kmalloc(buf_len, GFP_KERNEL);
|
||||
if (!buf)
|
||||
goto free_req;
|
||||
|
||||
crypto_ecdh_encode_key(buf, buf_len, &p);
|
||||
|
||||
/* Set A private Key */
|
||||
err = crypto_kpp_set_secret(tfm, (void *)buf, buf_len);
|
||||
if (err)
|
||||
goto free_all;
|
||||
|
||||
swap_digits((u64 *)public_key, (u64 *)tmp, 4); /* x */
|
||||
swap_digits((u64 *)&public_key[32], (u64 *)&tmp[32], 4); /* y */
|
||||
|
||||
|
@ -123,104 +102,129 @@ bool compute_ecdh_secret(const u8 public_key[64], const u8 private_key[32],
|
|||
memcpy(secret, tmp, 32);
|
||||
|
||||
free_all:
|
||||
kzfree(buf);
|
||||
free_req:
|
||||
kpp_request_free(req);
|
||||
free_kpp:
|
||||
crypto_free_kpp(tfm);
|
||||
free_tmp:
|
||||
kfree(tmp);
|
||||
return (err == 0);
|
||||
kzfree(tmp);
|
||||
return err;
|
||||
}
|
||||
|
||||
bool generate_ecdh_keys(u8 public_key[64], u8 private_key[32])
|
||||
/* set_ecdh_privkey() - set or generate ecc private key.
|
||||
*
|
||||
* Function generates an ecc private key in the crypto subsystem when receiving
|
||||
* a NULL private key or sets the received key when not NULL.
|
||||
*
|
||||
* @tfm: KPP tfm handle allocated with crypto_alloc_kpp().
|
||||
* @private_key: user's ecc private key. When not NULL, the key is expected
|
||||
* in little endian format.
|
||||
*
|
||||
* Return: zero on success; error code in case of error.
|
||||
*/
|
||||
int set_ecdh_privkey(struct crypto_kpp *tfm, const u8 private_key[32])
|
||||
{
|
||||
struct crypto_kpp *tfm;
|
||||
struct kpp_request *req;
|
||||
struct ecdh p;
|
||||
struct ecdh_completion result;
|
||||
struct scatterlist dst;
|
||||
u8 *tmp, *buf;
|
||||
u8 *buf, *tmp = NULL;
|
||||
unsigned int buf_len;
|
||||
int err = -ENOMEM;
|
||||
const unsigned short max_tries = 16;
|
||||
unsigned short tries = 0;
|
||||
int err;
|
||||
struct ecdh p = {0};
|
||||
|
||||
tmp = kmalloc(64, GFP_KERNEL);
|
||||
if (!tmp)
|
||||
return false;
|
||||
p.curve_id = ECC_CURVE_NIST_P256;
|
||||
|
||||
tfm = crypto_alloc_kpp("ecdh", CRYPTO_ALG_INTERNAL, 0);
|
||||
if (IS_ERR(tfm)) {
|
||||
pr_err("alg: kpp: Failed to load tfm for kpp: %ld\n",
|
||||
PTR_ERR(tfm));
|
||||
if (private_key) {
|
||||
tmp = kmalloc(32, GFP_KERNEL);
|
||||
if (!tmp)
|
||||
return -ENOMEM;
|
||||
swap_digits((u64 *)private_key, (u64 *)tmp, 4);
|
||||
p.key = tmp;
|
||||
p.key_size = 32;
|
||||
}
|
||||
|
||||
buf_len = crypto_ecdh_key_len(&p);
|
||||
buf = kmalloc(buf_len, GFP_KERNEL);
|
||||
if (!buf) {
|
||||
err = -ENOMEM;
|
||||
goto free_tmp;
|
||||
}
|
||||
|
||||
err = crypto_ecdh_encode_key(buf, buf_len, &p);
|
||||
if (err)
|
||||
goto free_all;
|
||||
|
||||
err = crypto_kpp_set_secret(tfm, buf, buf_len);
|
||||
/* fall through */
|
||||
free_all:
|
||||
kzfree(buf);
|
||||
free_tmp:
|
||||
kzfree(tmp);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* generate_ecdh_public_key() - function assumes that the private key was
|
||||
* already set.
|
||||
*
|
||||
* @tfm: KPP tfm handle allocated with crypto_alloc_kpp().
|
||||
* @public_key: memory where the computed ecc public key will be saved.
|
||||
*
|
||||
* Return: zero on success; error code in case of error.
|
||||
*/
|
||||
int generate_ecdh_public_key(struct crypto_kpp *tfm, u8 public_key[64])
|
||||
{
|
||||
struct kpp_request *req;
|
||||
u8 *tmp;
|
||||
struct ecdh_completion result;
|
||||
struct scatterlist dst;
|
||||
int err;
|
||||
|
||||
tmp = kmalloc(64, GFP_KERNEL);
|
||||
if (!tmp)
|
||||
return -ENOMEM;
|
||||
|
||||
req = kpp_request_alloc(tfm, GFP_KERNEL);
|
||||
if (!req)
|
||||
goto free_kpp;
|
||||
if (!req) {
|
||||
err = -ENOMEM;
|
||||
goto free_tmp;
|
||||
}
|
||||
|
||||
init_completion(&result.completion);
|
||||
sg_init_one(&dst, tmp, 64);
|
||||
kpp_request_set_input(req, NULL, 0);
|
||||
kpp_request_set_output(req, &dst, 64);
|
||||
kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
|
||||
ecdh_complete, &result);
|
||||
|
||||
/* Set curve_id */
|
||||
p.curve_id = ECC_CURVE_NIST_P256;
|
||||
p.key_size = 32;
|
||||
buf_len = crypto_ecdh_key_len(&p);
|
||||
buf = kmalloc(buf_len, GFP_KERNEL);
|
||||
if (!buf)
|
||||
goto free_req;
|
||||
err = crypto_kpp_generate_public_key(req);
|
||||
if (err == -EINPROGRESS) {
|
||||
wait_for_completion(&result.completion);
|
||||
err = result.err;
|
||||
}
|
||||
if (err < 0)
|
||||
goto free_all;
|
||||
|
||||
do {
|
||||
if (tries++ >= max_tries)
|
||||
goto free_all;
|
||||
|
||||
/* Set private Key */
|
||||
p.key = (char *)private_key;
|
||||
crypto_ecdh_encode_key(buf, buf_len, &p);
|
||||
err = crypto_kpp_set_secret(tfm, buf, buf_len);
|
||||
if (err)
|
||||
goto free_all;
|
||||
|
||||
sg_init_one(&dst, tmp, 64);
|
||||
kpp_request_set_input(req, NULL, 0);
|
||||
kpp_request_set_output(req, &dst, 64);
|
||||
kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
|
||||
ecdh_complete, &result);
|
||||
|
||||
err = crypto_kpp_generate_public_key(req);
|
||||
|
||||
if (err == -EINPROGRESS) {
|
||||
wait_for_completion(&result.completion);
|
||||
err = result.err;
|
||||
}
|
||||
|
||||
/* Private key is not valid. Regenerate */
|
||||
if (err == -EINVAL)
|
||||
continue;
|
||||
|
||||
if (err < 0)
|
||||
goto free_all;
|
||||
else
|
||||
break;
|
||||
|
||||
} while (true);
|
||||
|
||||
/* Keys are handed back in little endian as expected by Security
|
||||
* Manager Protocol
|
||||
/* The public key is handed back in little endian as expected by
|
||||
* the Security Manager Protocol.
|
||||
*/
|
||||
swap_digits((u64 *)tmp, (u64 *)public_key, 4); /* x */
|
||||
swap_digits((u64 *)&tmp[32], (u64 *)&public_key[32], 4); /* y */
|
||||
swap_digits((u64 *)private_key, (u64 *)tmp, 4);
|
||||
memcpy(private_key, tmp, 32);
|
||||
|
||||
free_all:
|
||||
kzfree(buf);
|
||||
free_req:
|
||||
kpp_request_free(req);
|
||||
free_kpp:
|
||||
crypto_free_kpp(tfm);
|
||||
free_tmp:
|
||||
kfree(tmp);
|
||||
return (err == 0);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* generate_ecdh_keys() - generate ecc key pair.
|
||||
*
|
||||
* @tfm: KPP tfm handle allocated with crypto_alloc_kpp().
|
||||
* @public_key: memory where the computed ecc public key will be saved.
|
||||
*
|
||||
* Return: zero on success; error code in case of error.
|
||||
*/
|
||||
int generate_ecdh_keys(struct crypto_kpp *tfm, u8 public_key[64])
|
||||
{
|
||||
int err;
|
||||
|
||||
err = set_ecdh_privkey(tfm, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return generate_ecdh_public_key(tfm, public_key);
|
||||
}
|
||||
|
|
|
@ -20,8 +20,11 @@
|
|||
* COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
|
||||
* SOFTWARE IS DISCLAIMED.
|
||||
*/
|
||||
#include <crypto/kpp.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
bool compute_ecdh_secret(const u8 pub_a[64], const u8 priv_b[32],
|
||||
u8 secret[32]);
|
||||
bool generate_ecdh_keys(u8 public_key[64], u8 private_key[32]);
|
||||
int compute_ecdh_secret(struct crypto_kpp *tfm, const u8 pair_public_key[64],
|
||||
u8 secret[32]);
|
||||
int set_ecdh_privkey(struct crypto_kpp *tfm, const u8 *private_key);
|
||||
int generate_ecdh_public_key(struct crypto_kpp *tfm, u8 public_key[64]);
|
||||
int generate_ecdh_keys(struct crypto_kpp *tfm, u8 public_key[64]);
|
||||
|
|
|
@ -138,12 +138,12 @@ static const u8 dhkey_3[32] __initconst = {
|
|||
0x7c, 0x1c, 0xf9, 0x49, 0xe6, 0xd7, 0xaa, 0x70,
|
||||
};
|
||||
|
||||
static int __init test_ecdh_sample(const u8 priv_a[32], const u8 priv_b[32],
|
||||
const u8 pub_a[64], const u8 pub_b[64],
|
||||
const u8 dhkey[32])
|
||||
static int __init test_ecdh_sample(struct crypto_kpp *tfm, const u8 priv_a[32],
|
||||
const u8 priv_b[32], const u8 pub_a[64],
|
||||
const u8 pub_b[64], const u8 dhkey[32])
|
||||
{
|
||||
u8 *tmp, *dhkey_a, *dhkey_b;
|
||||
int ret = 0;
|
||||
int ret;
|
||||
|
||||
tmp = kmalloc(64, GFP_KERNEL);
|
||||
if (!tmp)
|
||||
|
@ -152,17 +152,30 @@ static int __init test_ecdh_sample(const u8 priv_a[32], const u8 priv_b[32],
|
|||
dhkey_a = &tmp[0];
|
||||
dhkey_b = &tmp[32];
|
||||
|
||||
compute_ecdh_secret(pub_b, priv_a, dhkey_a);
|
||||
compute_ecdh_secret(pub_a, priv_b, dhkey_b);
|
||||
ret = set_ecdh_privkey(tfm, priv_a);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = compute_ecdh_secret(tfm, pub_b, dhkey_a);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (memcmp(dhkey_a, dhkey, 32)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = set_ecdh_privkey(tfm, priv_b);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = compute_ecdh_secret(tfm, pub_a, dhkey_b);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (memcmp(dhkey_b, dhkey, 32))
|
||||
ret = -EINVAL;
|
||||
|
||||
/* fall through*/
|
||||
out:
|
||||
kfree(tmp);
|
||||
return ret;
|
||||
|
@ -185,30 +198,43 @@ static const struct file_operations test_ecdh_fops = {
|
|||
|
||||
static int __init test_ecdh(void)
|
||||
{
|
||||
struct crypto_kpp *tfm;
|
||||
ktime_t calltime, delta, rettime;
|
||||
unsigned long long duration;
|
||||
unsigned long long duration = 0;
|
||||
int err;
|
||||
|
||||
calltime = ktime_get();
|
||||
|
||||
err = test_ecdh_sample(priv_a_1, priv_b_1, pub_a_1, pub_b_1, dhkey_1);
|
||||
tfm = crypto_alloc_kpp("ecdh", CRYPTO_ALG_INTERNAL, 0);
|
||||
if (IS_ERR(tfm)) {
|
||||
BT_ERR("Unable to create ECDH crypto context");
|
||||
err = PTR_ERR(tfm);
|
||||
goto done;
|
||||
}
|
||||
|
||||
err = test_ecdh_sample(tfm, priv_a_1, priv_b_1, pub_a_1, pub_b_1,
|
||||
dhkey_1);
|
||||
if (err) {
|
||||
BT_ERR("ECDH sample 1 failed");
|
||||
goto done;
|
||||
}
|
||||
|
||||
err = test_ecdh_sample(priv_a_2, priv_b_2, pub_a_2, pub_b_2, dhkey_2);
|
||||
err = test_ecdh_sample(tfm, priv_a_2, priv_b_2, pub_a_2, pub_b_2,
|
||||
dhkey_2);
|
||||
if (err) {
|
||||
BT_ERR("ECDH sample 2 failed");
|
||||
goto done;
|
||||
}
|
||||
|
||||
err = test_ecdh_sample(priv_a_3, priv_a_3, pub_a_3, pub_a_3, dhkey_3);
|
||||
err = test_ecdh_sample(tfm, priv_a_3, priv_a_3, pub_a_3, pub_a_3,
|
||||
dhkey_3);
|
||||
if (err) {
|
||||
BT_ERR("ECDH sample 3 failed");
|
||||
goto done;
|
||||
}
|
||||
|
||||
crypto_free_kpp(tfm);
|
||||
|
||||
rettime = ktime_get();
|
||||
delta = ktime_sub(rettime, calltime);
|
||||
duration = (unsigned long long) ktime_to_ns(delta) >> 10;
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <crypto/algapi.h>
|
||||
#include <crypto/b128ops.h>
|
||||
#include <crypto/hash.h>
|
||||
#include <crypto/kpp.h>
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
|
@ -83,7 +84,6 @@ enum {
|
|||
struct smp_dev {
|
||||
/* Secure Connections OOB data */
|
||||
u8 local_pk[64];
|
||||
u8 local_sk[32];
|
||||
u8 local_rand[16];
|
||||
bool debug_key;
|
||||
|
||||
|
@ -92,6 +92,7 @@ struct smp_dev {
|
|||
|
||||
struct crypto_cipher *tfm_aes;
|
||||
struct crypto_shash *tfm_cmac;
|
||||
struct crypto_kpp *tfm_ecdh;
|
||||
};
|
||||
|
||||
struct smp_chan {
|
||||
|
@ -124,13 +125,13 @@ struct smp_chan {
|
|||
|
||||
/* Secure Connections variables */
|
||||
u8 local_pk[64];
|
||||
u8 local_sk[32];
|
||||
u8 remote_pk[64];
|
||||
u8 dhkey[32];
|
||||
u8 mackey[16];
|
||||
|
||||
struct crypto_cipher *tfm_aes;
|
||||
struct crypto_shash *tfm_cmac;
|
||||
struct crypto_kpp *tfm_ecdh;
|
||||
};
|
||||
|
||||
/* These debug key values are defined in the SMP section of the core
|
||||
|
@ -565,22 +566,22 @@ int smp_generate_oob(struct hci_dev *hdev, u8 hash[16], u8 rand[16])
|
|||
|
||||
if (hci_dev_test_flag(hdev, HCI_USE_DEBUG_KEYS)) {
|
||||
BT_DBG("Using debug keys");
|
||||
err = set_ecdh_privkey(smp->tfm_ecdh, debug_sk);
|
||||
if (err)
|
||||
return err;
|
||||
memcpy(smp->local_pk, debug_pk, 64);
|
||||
memcpy(smp->local_sk, debug_sk, 32);
|
||||
smp->debug_key = true;
|
||||
} else {
|
||||
while (true) {
|
||||
/* Seed private key with random number */
|
||||
get_random_bytes(smp->local_sk, 32);
|
||||
|
||||
/* Generate local key pair for Secure Connections */
|
||||
if (!generate_ecdh_keys(smp->local_pk, smp->local_sk))
|
||||
return -EIO;
|
||||
/* Generate key pair for Secure Connections */
|
||||
err = generate_ecdh_keys(smp->tfm_ecdh, smp->local_pk);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* This is unlikely, but we need to check that
|
||||
* we didn't accidentially generate a debug key.
|
||||
*/
|
||||
if (crypto_memneq(smp->local_sk, debug_sk, 32))
|
||||
if (crypto_memneq(smp->local_pk, debug_pk, 64))
|
||||
break;
|
||||
}
|
||||
smp->debug_key = false;
|
||||
|
@ -588,7 +589,6 @@ int smp_generate_oob(struct hci_dev *hdev, u8 hash[16], u8 rand[16])
|
|||
|
||||
SMP_DBG("OOB Public Key X: %32phN", smp->local_pk);
|
||||
SMP_DBG("OOB Public Key Y: %32phN", smp->local_pk + 32);
|
||||
SMP_DBG("OOB Private Key: %32phN", smp->local_sk);
|
||||
|
||||
get_random_bytes(smp->local_rand, 16);
|
||||
|
||||
|
@ -771,6 +771,7 @@ static void smp_chan_destroy(struct l2cap_conn *conn)
|
|||
|
||||
crypto_free_cipher(smp->tfm_aes);
|
||||
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
|
||||
* support hasn't been explicitly enabled.
|
||||
|
@ -1391,16 +1392,19 @@ static struct smp_chan *smp_chan_create(struct l2cap_conn *conn)
|
|||
smp->tfm_aes = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(smp->tfm_aes)) {
|
||||
BT_ERR("Unable to create AES crypto context");
|
||||
kzfree(smp);
|
||||
return NULL;
|
||||
goto zfree_smp;
|
||||
}
|
||||
|
||||
smp->tfm_cmac = crypto_alloc_shash("cmac(aes)", 0, 0);
|
||||
if (IS_ERR(smp->tfm_cmac)) {
|
||||
BT_ERR("Unable to create CMAC crypto context");
|
||||
crypto_free_cipher(smp->tfm_aes);
|
||||
kzfree(smp);
|
||||
return NULL;
|
||||
goto free_cipher;
|
||||
}
|
||||
|
||||
smp->tfm_ecdh = crypto_alloc_kpp("ecdh", CRYPTO_ALG_INTERNAL, 0);
|
||||
if (IS_ERR(smp->tfm_ecdh)) {
|
||||
BT_ERR("Unable to create ECDH crypto context");
|
||||
goto free_shash;
|
||||
}
|
||||
|
||||
smp->conn = conn;
|
||||
|
@ -1413,6 +1417,14 @@ static struct smp_chan *smp_chan_create(struct l2cap_conn *conn)
|
|||
hci_conn_hold(conn->hcon);
|
||||
|
||||
return smp;
|
||||
|
||||
free_shash:
|
||||
crypto_free_shash(smp->tfm_cmac);
|
||||
free_cipher:
|
||||
crypto_free_cipher(smp->tfm_aes);
|
||||
zfree_smp:
|
||||
kzfree(smp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int sc_mackey_and_ltk(struct smp_chan *smp, u8 mackey[16], u8 ltk[16])
|
||||
|
@ -1883,7 +1895,6 @@ static u8 sc_send_public_key(struct smp_chan *smp)
|
|||
smp_dev = chan->data;
|
||||
|
||||
memcpy(smp->local_pk, smp_dev->local_pk, 64);
|
||||
memcpy(smp->local_sk, smp_dev->local_sk, 32);
|
||||
memcpy(smp->lr, smp_dev->local_rand, 16);
|
||||
|
||||
if (smp_dev->debug_key)
|
||||
|
@ -1894,22 +1905,20 @@ static u8 sc_send_public_key(struct smp_chan *smp)
|
|||
|
||||
if (hci_dev_test_flag(hdev, HCI_USE_DEBUG_KEYS)) {
|
||||
BT_DBG("Using debug keys");
|
||||
if (set_ecdh_privkey(smp->tfm_ecdh, debug_sk))
|
||||
return SMP_UNSPECIFIED;
|
||||
memcpy(smp->local_pk, debug_pk, 64);
|
||||
memcpy(smp->local_sk, debug_sk, 32);
|
||||
set_bit(SMP_FLAG_DEBUG_KEY, &smp->flags);
|
||||
} else {
|
||||
while (true) {
|
||||
/* Seed private key with random number */
|
||||
get_random_bytes(smp->local_sk, 32);
|
||||
|
||||
/* Generate local key pair for Secure Connections */
|
||||
if (!generate_ecdh_keys(smp->local_pk, smp->local_sk))
|
||||
/* Generate key pair for Secure Connections */
|
||||
if (generate_ecdh_keys(smp->tfm_ecdh, smp->local_pk))
|
||||
return SMP_UNSPECIFIED;
|
||||
|
||||
/* This is unlikely, but we need to check that
|
||||
* we didn't accidentially generate a debug key.
|
||||
*/
|
||||
if (crypto_memneq(smp->local_sk, debug_sk, 32))
|
||||
if (crypto_memneq(smp->local_pk, debug_pk, 64))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1917,7 +1926,6 @@ static u8 sc_send_public_key(struct smp_chan *smp)
|
|||
done:
|
||||
SMP_DBG("Local Public Key X: %32phN", smp->local_pk);
|
||||
SMP_DBG("Local Public Key Y: %32phN", smp->local_pk + 32);
|
||||
SMP_DBG("Local Private Key: %32phN", smp->local_sk);
|
||||
|
||||
smp_send_cmd(smp->conn, SMP_CMD_PUBLIC_KEY, 64, smp->local_pk);
|
||||
|
||||
|
@ -2645,6 +2653,7 @@ static int smp_cmd_public_key(struct l2cap_conn *conn, struct sk_buff *skb)
|
|||
struct l2cap_chan *chan = conn->smp;
|
||||
struct smp_chan *smp = chan->data;
|
||||
struct hci_dev *hdev = hcon->hdev;
|
||||
struct crypto_kpp *tfm_ecdh;
|
||||
struct smp_cmd_pairing_confirm cfm;
|
||||
int err;
|
||||
|
||||
|
@ -2677,7 +2686,18 @@ static int smp_cmd_public_key(struct l2cap_conn *conn, struct sk_buff *skb)
|
|||
SMP_DBG("Remote Public Key X: %32phN", smp->remote_pk);
|
||||
SMP_DBG("Remote Public Key Y: %32phN", smp->remote_pk + 32);
|
||||
|
||||
if (!compute_ecdh_secret(smp->remote_pk, smp->local_sk, smp->dhkey))
|
||||
/* Compute the shared secret on the same crypto tfm on which the private
|
||||
* key was set/generated.
|
||||
*/
|
||||
if (test_bit(SMP_FLAG_LOCAL_OOB, &smp->flags)) {
|
||||
struct smp_dev *smp_dev = chan->data;
|
||||
|
||||
tfm_ecdh = smp_dev->tfm_ecdh;
|
||||
} else {
|
||||
tfm_ecdh = smp->tfm_ecdh;
|
||||
}
|
||||
|
||||
if (compute_ecdh_secret(tfm_ecdh, smp->remote_pk, smp->dhkey))
|
||||
return SMP_UNSPECIFIED;
|
||||
|
||||
SMP_DBG("DHKey %32phN", smp->dhkey);
|
||||
|
@ -3169,6 +3189,7 @@ static struct l2cap_chan *smp_add_cid(struct hci_dev *hdev, u16 cid)
|
|||
struct smp_dev *smp;
|
||||
struct crypto_cipher *tfm_aes;
|
||||
struct crypto_shash *tfm_cmac;
|
||||
struct crypto_kpp *tfm_ecdh;
|
||||
|
||||
if (cid == L2CAP_CID_SMP_BREDR) {
|
||||
smp = NULL;
|
||||
|
@ -3194,8 +3215,18 @@ static struct l2cap_chan *smp_add_cid(struct hci_dev *hdev, u16 cid)
|
|||
return ERR_CAST(tfm_cmac);
|
||||
}
|
||||
|
||||
tfm_ecdh = crypto_alloc_kpp("ecdh", CRYPTO_ALG_INTERNAL, 0);
|
||||
if (IS_ERR(tfm_ecdh)) {
|
||||
BT_ERR("Unable to create ECDH crypto context");
|
||||
crypto_free_shash(tfm_cmac);
|
||||
crypto_free_cipher(tfm_aes);
|
||||
kzfree(smp);
|
||||
return ERR_CAST(tfm_ecdh);
|
||||
}
|
||||
|
||||
smp->tfm_aes = tfm_aes;
|
||||
smp->tfm_cmac = tfm_cmac;
|
||||
smp->tfm_ecdh = tfm_ecdh;
|
||||
smp->min_key_size = SMP_MIN_ENC_KEY_SIZE;
|
||||
smp->max_key_size = SMP_MAX_ENC_KEY_SIZE;
|
||||
|
||||
|
@ -3205,6 +3236,7 @@ static struct l2cap_chan *smp_add_cid(struct hci_dev *hdev, u16 cid)
|
|||
if (smp) {
|
||||
crypto_free_cipher(smp->tfm_aes);
|
||||
crypto_free_shash(smp->tfm_cmac);
|
||||
crypto_free_kpp(smp->tfm_ecdh);
|
||||
kzfree(smp);
|
||||
}
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
@ -3252,6 +3284,7 @@ static void smp_del_chan(struct l2cap_chan *chan)
|
|||
chan->data = NULL;
|
||||
crypto_free_cipher(smp->tfm_aes);
|
||||
crypto_free_shash(smp->tfm_cmac);
|
||||
crypto_free_kpp(smp->tfm_ecdh);
|
||||
kzfree(smp);
|
||||
}
|
||||
|
||||
|
@ -3490,25 +3523,18 @@ void smp_unregister(struct hci_dev *hdev)
|
|||
|
||||
#if IS_ENABLED(CONFIG_BT_SELFTEST_SMP)
|
||||
|
||||
static inline void swap_digits(u64 *in, u64 *out, unsigned int ndigits)
|
||||
static int __init test_debug_key(struct crypto_kpp *tfm_ecdh)
|
||||
{
|
||||
int i;
|
||||
u8 pk[64];
|
||||
int err;
|
||||
|
||||
for (i = 0; i < ndigits; i++)
|
||||
out[i] = __swab64(in[ndigits - 1 - i]);
|
||||
}
|
||||
err = set_ecdh_privkey(tfm_ecdh, debug_sk);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
static int __init test_debug_key(void)
|
||||
{
|
||||
u8 pk[64], sk[32];
|
||||
|
||||
swap_digits((u64 *)debug_sk, (u64 *)sk, 4);
|
||||
|
||||
if (!generate_ecdh_keys(pk, sk))
|
||||
return -EINVAL;
|
||||
|
||||
if (crypto_memneq(sk, debug_sk, 32))
|
||||
return -EINVAL;
|
||||
err = generate_ecdh_public_key(tfm_ecdh, pk);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (crypto_memneq(pk, debug_pk, 64))
|
||||
return -EINVAL;
|
||||
|
@ -3763,7 +3789,8 @@ static const struct file_operations test_smp_fops = {
|
|||
};
|
||||
|
||||
static int __init run_selftests(struct crypto_cipher *tfm_aes,
|
||||
struct crypto_shash *tfm_cmac)
|
||||
struct crypto_shash *tfm_cmac,
|
||||
struct crypto_kpp *tfm_ecdh)
|
||||
{
|
||||
ktime_t calltime, delta, rettime;
|
||||
unsigned long long duration;
|
||||
|
@ -3771,7 +3798,7 @@ static int __init run_selftests(struct crypto_cipher *tfm_aes,
|
|||
|
||||
calltime = ktime_get();
|
||||
|
||||
err = test_debug_key();
|
||||
err = test_debug_key(tfm_ecdh);
|
||||
if (err) {
|
||||
BT_ERR("debug_key test failed");
|
||||
goto done;
|
||||
|
@ -3848,6 +3875,7 @@ int __init bt_selftest_smp(void)
|
|||
{
|
||||
struct crypto_cipher *tfm_aes;
|
||||
struct crypto_shash *tfm_cmac;
|
||||
struct crypto_kpp *tfm_ecdh;
|
||||
int err;
|
||||
|
||||
tfm_aes = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC);
|
||||
|
@ -3863,10 +3891,19 @@ int __init bt_selftest_smp(void)
|
|||
return PTR_ERR(tfm_cmac);
|
||||
}
|
||||
|
||||
err = run_selftests(tfm_aes, tfm_cmac);
|
||||
tfm_ecdh = crypto_alloc_kpp("ecdh", CRYPTO_ALG_INTERNAL, 0);
|
||||
if (IS_ERR(tfm_ecdh)) {
|
||||
BT_ERR("Unable to create ECDH crypto context");
|
||||
crypto_free_shash(tfm_cmac);
|
||||
crypto_free_cipher(tfm_aes);
|
||||
return PTR_ERR(tfm_ecdh);
|
||||
}
|
||||
|
||||
err = run_selftests(tfm_aes, tfm_cmac, tfm_ecdh);
|
||||
|
||||
crypto_free_shash(tfm_cmac);
|
||||
crypto_free_cipher(tfm_aes);
|
||||
crypto_free_kpp(tfm_ecdh);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue