mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2025-01-18 17:46:15 +07:00
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-04-14 Here's the main batch of Bluetooth & 802.15.4 patches for the 4.12 kernel. - Many fixes to 6LoWPAN, in particular for BLE - New CA8210 IEEE 802.15.4 device driver (accounting for most of the lines of code added in this pull request) - Added Nokia Bluetooth (UART) HCI driver - Some serdev & TTY changes that are dependencies for the Nokia driver (with acks from relevant maintainers and an agreement that these come through the bluetooth tree) - Support for new Intel Bluetooth device - Various other minor cleanups/fixes here and there 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
450cc8cce2
28
Documentation/devicetree/bindings/net/ieee802154/ca8210.txt
Normal file
28
Documentation/devicetree/bindings/net/ieee802154/ca8210.txt
Normal file
@ -0,0 +1,28 @@
|
||||
* CA8210 IEEE 802.15.4 *
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "cascoda,ca8210"
|
||||
- reg: Controlling chip select
|
||||
- spi-max-frequency: Maximum clock speed, should be *less than*
|
||||
4000000
|
||||
- spi-cpol: Requires inverted clock polarity
|
||||
- reset-gpio: GPIO attached to reset
|
||||
- irq-gpio: GPIO attached to IRQ
|
||||
Optional properties:
|
||||
- extclock-enable: Include for the ca8210 to route its 16MHz clock
|
||||
to an output
|
||||
- extclock-freq: Frequency in Hz of the external clock
|
||||
- extclock-gpio: GPIO of the ca8210 to output the clock on
|
||||
|
||||
Example:
|
||||
ca8210@0 {
|
||||
compatible = "cascoda,ca8210";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <3000000>;
|
||||
spi-cpol;
|
||||
reset-gpio = <&gpio1 1 GPIO_ACTIVE_HIGH>;
|
||||
irq-gpio = <&gpio1 2 GPIO_ACTIVE_HIGH>;
|
||||
extclock-enable;
|
||||
extclock-freq = 16000000;
|
||||
extclock-gpio = 2;
|
||||
};
|
51
Documentation/devicetree/bindings/net/nokia-bluetooth.txt
Normal file
51
Documentation/devicetree/bindings/net/nokia-bluetooth.txt
Normal file
@ -0,0 +1,51 @@
|
||||
Nokia Bluetooth Chips
|
||||
---------------------
|
||||
|
||||
Nokia phones often come with UART connected bluetooth chips from different
|
||||
vendors and modified device API. Those devices speak a protocol named H4+
|
||||
(also known as h4p) by Nokia, which is similar to the H4 protocol from the
|
||||
Bluetooth standard. In addition to the H4 protocol it specifies two more
|
||||
UART status lines for wakeup of UART transceivers to improve power management
|
||||
and a few new packet types used to negotiate uart speed.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: should contain "nokia,h4p-bluetooth" as well as one of the following:
|
||||
* "brcm,bcm2048-nokia"
|
||||
* "ti,wl1271-bluetooth-nokia"
|
||||
- reset-gpios: GPIO specifier, used to reset the BT module (active low)
|
||||
- bluetooth-wakeup-gpios: GPIO specifier, used to wakeup the BT module (active high)
|
||||
- host-wakeup-gpios: GPIO specifier, used to wakeup the host processor (active high)
|
||||
- clock-names: should be "sysclk"
|
||||
- clocks: should contain a clock specifier for every name in clock-names
|
||||
|
||||
Optional properties:
|
||||
|
||||
- None
|
||||
|
||||
Example:
|
||||
|
||||
/ {
|
||||
/* controlled (enabled/disabled) directly by BT module */
|
||||
bluetooth_clk: vctcxo {
|
||||
compatible = "fixed-clock";
|
||||
#clock-cells = <0>;
|
||||
clock-frequency = <38400000>;
|
||||
};
|
||||
};
|
||||
|
||||
&uart2 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&uart2_pins>;
|
||||
|
||||
bluetooth {
|
||||
compatible = "ti,wl1271-bluetooth-nokia", "nokia,h4p-bluetooth";
|
||||
|
||||
reset-gpios = <&gpio1 26 GPIO_ACTIVE_LOW>; /* gpio26 */
|
||||
host-wakeup-gpios = <&gpio4 5 GPIO_ACTIVE_HIGH>; /* gpio101 */
|
||||
bluetooth-wakeup-gpios = <&gpio2 5 GPIO_ACTIVE_HIGH>; /* gpio37 */
|
||||
|
||||
clocks = <&bluetooth_clk>;
|
||||
clock-names = "sysclk";
|
||||
};
|
||||
};
|
35
Documentation/devicetree/bindings/net/ti,wilink-st.txt
Normal file
35
Documentation/devicetree/bindings/net/ti,wilink-st.txt
Normal file
@ -0,0 +1,35 @@
|
||||
TI WiLink 7/8 (wl12xx/wl18xx) Shared Transport BT/FM/GPS devices
|
||||
|
||||
TI WiLink devices have a UART interface for providing Bluetooth, FM radio,
|
||||
and GPS over what's called "shared transport". The shared transport is
|
||||
standard BT HCI protocol with additional channels for the other functions.
|
||||
|
||||
These devices also have a separate WiFi interface as described in
|
||||
wireless/ti,wlcore.txt.
|
||||
|
||||
This bindings follows the UART slave device binding in
|
||||
../serial/slave-device.txt.
|
||||
|
||||
Required properties:
|
||||
- compatible: should be one of the following:
|
||||
"ti,wl1271-st"
|
||||
"ti,wl1273-st"
|
||||
"ti,wl1831-st"
|
||||
"ti,wl1835-st"
|
||||
"ti,wl1837-st"
|
||||
|
||||
Optional properties:
|
||||
- enable-gpios : GPIO signal controlling enabling of BT. Active high.
|
||||
- vio-supply : Vio input supply (1.8V)
|
||||
- vbat-supply : Vbat input supply (2.9-4.8V)
|
||||
|
||||
Example:
|
||||
|
||||
&serial0 {
|
||||
compatible = "ns16550a";
|
||||
...
|
||||
bluetooth {
|
||||
compatible = "ti,wl1835-st";
|
||||
enable-gpios = <&gpio1 7 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
};
|
@ -51,6 +51,7 @@ brcm Broadcom Corporation
|
||||
buffalo Buffalo, Inc.
|
||||
calxeda Calxeda
|
||||
capella Capella Microsystems, Inc
|
||||
cascoda Cascoda, Ltd.
|
||||
cavium Cavium, Inc.
|
||||
cdns Cadence Design Systems Inc.
|
||||
ceva Ceva, Inc.
|
||||
|
@ -2944,6 +2944,15 @@ W: http://www.linux-c6x.org/wiki/index.php/Main_Page
|
||||
S: Maintained
|
||||
F: arch/c6x/
|
||||
|
||||
CA8210 IEEE-802.15.4 RADIO DRIVER
|
||||
M: Harry Morris <h.morris@cascoda.com>
|
||||
M: linuxdev@cascoda.com
|
||||
L: linux-wpan@vger.kernel.org
|
||||
W: https://github.com/Cascoda/ca8210-linux.git
|
||||
S: Maintained
|
||||
F: drivers/net/ieee802154/ca8210.c
|
||||
F: Documentation/devicetree/bindings/net/ieee802154/ca8210.txt
|
||||
|
||||
CACHEFILES: FS-CACHE BACKEND FOR CACHING ON MOUNTED FILESYSTEMS
|
||||
M: David Howells <dhowells@redhat.com>
|
||||
L: linux-cachefs@redhat.com (moderated for non-subscribers)
|
||||
|
@ -98,6 +98,11 @@ uart1: uart@f7111000 {
|
||||
assigned-clocks = <&sys_ctrl HI6220_UART1_SRC>;
|
||||
assigned-clock-rates = <150000000>;
|
||||
status = "ok";
|
||||
|
||||
bluetooth {
|
||||
compatible = "ti,wl1835-st";
|
||||
enable-gpios = <&gpio1 7 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
};
|
||||
|
||||
uart2: uart@f7112000 {
|
||||
|
@ -86,6 +86,18 @@ config BT_HCIUART_H4
|
||||
|
||||
Say Y here to compile support for HCI UART (H4) protocol.
|
||||
|
||||
config BT_HCIUART_NOKIA
|
||||
tristate "UART Nokia H4+ protocol support"
|
||||
depends on BT_HCIUART
|
||||
depends on SERIAL_DEV_BUS
|
||||
depends on PM
|
||||
help
|
||||
Nokia H4+ is serial protocol for communication between Bluetooth
|
||||
device and host. This protocol is required for Bluetooth devices
|
||||
with UART interface in Nokia devices.
|
||||
|
||||
Say Y here to compile support for Nokia's H4+ protocol.
|
||||
|
||||
config BT_HCIUART_BCSP
|
||||
bool "BCSP protocol support"
|
||||
depends on BT_HCIUART
|
||||
|
@ -25,10 +25,13 @@ obj-$(CONFIG_BT_BCM) += btbcm.o
|
||||
obj-$(CONFIG_BT_RTL) += btrtl.o
|
||||
obj-$(CONFIG_BT_QCA) += btqca.o
|
||||
|
||||
obj-$(CONFIG_BT_HCIUART_NOKIA) += hci_nokia.o
|
||||
|
||||
btmrvl-y := btmrvl_main.o
|
||||
btmrvl-$(CONFIG_DEBUG_FS) += btmrvl_debugfs.o
|
||||
|
||||
hci_uart-y := hci_ldisc.o
|
||||
hci_uart-$(CONFIG_SERIAL_DEV_BUS) += hci_serdev.o
|
||||
hci_uart-$(CONFIG_BT_HCIUART_H4) += hci_h4.o
|
||||
hci_uart-$(CONFIG_BT_HCIUART_BCSP) += hci_bcsp.o
|
||||
hci_uart-$(CONFIG_BT_HCIUART_LL) += hci_ll.o
|
||||
|
@ -695,9 +695,8 @@ static int bluecard_open(struct bluecard_info *info)
|
||||
|
||||
spin_lock_init(&(info->lock));
|
||||
|
||||
init_timer(&(info->timer));
|
||||
info->timer.function = &bluecard_activity_led_timeout;
|
||||
info->timer.data = (u_long)info;
|
||||
setup_timer(&(info->timer), &bluecard_activity_led_timeout,
|
||||
(u_long)info);
|
||||
|
||||
skb_queue_head_init(&(info->txq));
|
||||
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/suspend.h>
|
||||
|
||||
#include <linux/mmc/sdio_ids.h>
|
||||
#include <linux/mmc/sdio_func.h>
|
||||
@ -60,13 +61,15 @@ static const struct of_device_id btmrvl_sdio_of_match_table[] = {
|
||||
|
||||
static irqreturn_t btmrvl_wake_irq_bt(int irq, void *priv)
|
||||
{
|
||||
struct btmrvl_plt_wake_cfg *cfg = priv;
|
||||
struct btmrvl_sdio_card *card = priv;
|
||||
struct btmrvl_plt_wake_cfg *cfg = card->plt_wake_cfg;
|
||||
|
||||
if (cfg->irq_bt >= 0) {
|
||||
pr_info("%s: wake by bt", __func__);
|
||||
cfg->wake_by_bt = true;
|
||||
disable_irq_nosync(irq);
|
||||
}
|
||||
pr_info("%s: wake by bt", __func__);
|
||||
cfg->wake_by_bt = true;
|
||||
disable_irq_nosync(irq);
|
||||
|
||||
pm_wakeup_event(&card->func->dev, 0);
|
||||
pm_system_wakeup();
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
@ -101,7 +104,7 @@ static int btmrvl_sdio_probe_of(struct device *dev,
|
||||
} else {
|
||||
ret = devm_request_irq(dev, cfg->irq_bt,
|
||||
btmrvl_wake_irq_bt,
|
||||
0, "bt_wake", cfg);
|
||||
0, "bt_wake", card);
|
||||
if (ret) {
|
||||
dev_err(dev,
|
||||
"Failed to request irq_bt %d (%d)\n",
|
||||
@ -1574,7 +1577,7 @@ static void btmrvl_sdio_remove(struct sdio_func *func)
|
||||
MODULE_SHUTDOWN_REQ);
|
||||
btmrvl_sdio_disable_host_int(card);
|
||||
}
|
||||
BT_DBG("unregester dev");
|
||||
BT_DBG("unregister dev");
|
||||
card->priv->surprise_removed = true;
|
||||
btmrvl_sdio_unregister_dev(card);
|
||||
btmrvl_remove_card(card->priv);
|
||||
@ -1625,6 +1628,13 @@ static int btmrvl_sdio_suspend(struct device *dev)
|
||||
if (priv->adapter->hs_state != HS_ACTIVATED) {
|
||||
if (btmrvl_enable_hs(priv)) {
|
||||
BT_ERR("HS not activated, suspend failed!");
|
||||
/* Disable platform specific wakeup interrupt */
|
||||
if (card->plt_wake_cfg &&
|
||||
card->plt_wake_cfg->irq_bt >= 0) {
|
||||
disable_irq_wake(card->plt_wake_cfg->irq_bt);
|
||||
disable_irq(card->plt_wake_cfg->irq_bt);
|
||||
}
|
||||
|
||||
priv->adapter->is_suspending = false;
|
||||
return -EBUSY;
|
||||
}
|
||||
@ -1637,10 +1647,10 @@ static int btmrvl_sdio_suspend(struct device *dev)
|
||||
if (priv->adapter->hs_state == HS_ACTIVATED) {
|
||||
BT_DBG("suspend with MMC_PM_KEEP_POWER");
|
||||
return sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
|
||||
} else {
|
||||
BT_DBG("suspend without MMC_PM_KEEP_POWER");
|
||||
return 0;
|
||||
}
|
||||
|
||||
BT_DBG("suspend without MMC_PM_KEEP_POWER");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btmrvl_sdio_resume(struct device *dev)
|
||||
|
@ -275,11 +275,8 @@ static int rtl_load_config(struct hci_dev *hdev, const char *name, u8 **buff)
|
||||
|
||||
BT_INFO("%s: rtl: loading %s", hdev->name, name);
|
||||
ret = request_firmware(&fw, name, &hdev->dev);
|
||||
if (ret < 0) {
|
||||
BT_ERR("%s: Failed to load %s", hdev->name, name);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = fw->size;
|
||||
*buff = kmemdup(fw->data, ret, GFP_KERNEL);
|
||||
|
||||
@ -331,6 +328,7 @@ static int btrtl_setup_rtl8723b(struct hci_dev *hdev, u16 lmp_subver,
|
||||
u8 *cfg_buff = NULL;
|
||||
u8 *tbuff;
|
||||
char *cfg_name = NULL;
|
||||
bool config_needed = false;
|
||||
|
||||
switch (lmp_subver) {
|
||||
case RTL_ROM_LMP_8723B:
|
||||
@ -344,6 +342,7 @@ static int btrtl_setup_rtl8723b(struct hci_dev *hdev, u16 lmp_subver,
|
||||
break;
|
||||
case RTL_ROM_LMP_8822B:
|
||||
cfg_name = "rtl_bt/rtl8822b_config.bin";
|
||||
config_needed = true;
|
||||
break;
|
||||
default:
|
||||
BT_ERR("%s: rtl: no config according to lmp_subver %04x",
|
||||
@ -353,8 +352,12 @@ static int btrtl_setup_rtl8723b(struct hci_dev *hdev, u16 lmp_subver,
|
||||
|
||||
if (cfg_name) {
|
||||
cfg_sz = rtl_load_config(hdev, cfg_name, &cfg_buff);
|
||||
if (cfg_sz < 0)
|
||||
if (cfg_sz < 0) {
|
||||
cfg_sz = 0;
|
||||
if (config_needed)
|
||||
BT_ERR("Necessary config file %s not found\n",
|
||||
cfg_name);
|
||||
}
|
||||
} else
|
||||
cfg_sz = 0;
|
||||
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
@ -262,6 +263,7 @@ static const struct usb_device_id blacklist_table[] = {
|
||||
{ USB_DEVICE(0x0cf3, 0xe007), .driver_info = BTUSB_QCA_ROME },
|
||||
{ USB_DEVICE(0x0cf3, 0xe009), .driver_info = BTUSB_QCA_ROME },
|
||||
{ USB_DEVICE(0x0cf3, 0xe300), .driver_info = BTUSB_QCA_ROME },
|
||||
{ USB_DEVICE(0x0cf3, 0xe301), .driver_info = BTUSB_QCA_ROME },
|
||||
{ USB_DEVICE(0x0cf3, 0xe360), .driver_info = BTUSB_QCA_ROME },
|
||||
{ USB_DEVICE(0x0489, 0xe092), .driver_info = BTUSB_QCA_ROME },
|
||||
{ USB_DEVICE(0x04ca, 0x3011), .driver_info = BTUSB_QCA_ROME },
|
||||
@ -328,6 +330,7 @@ static const struct usb_device_id blacklist_table[] = {
|
||||
{ USB_DEVICE(0x1286, 0x204e), .driver_info = BTUSB_MARVELL },
|
||||
|
||||
/* Intel Bluetooth devices */
|
||||
{ USB_DEVICE(0x8087, 0x0025), .driver_info = BTUSB_INTEL_NEW },
|
||||
{ USB_DEVICE(0x8087, 0x07da), .driver_info = BTUSB_CSR },
|
||||
{ USB_DEVICE(0x8087, 0x07dc), .driver_info = BTUSB_INTEL },
|
||||
{ USB_DEVICE(0x8087, 0x0a2a), .driver_info = BTUSB_INTEL },
|
||||
@ -2024,13 +2027,18 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* At the moment the iBT 3.0 hardware variants 0x0b (LnP/SfP)
|
||||
* and 0x0c (WsP) are supported by this firmware loading method.
|
||||
/* Check for supported iBT hardware variants of this firmware
|
||||
* loading method.
|
||||
*
|
||||
* This check has been put in place to ensure correct forward
|
||||
* compatibility options when newer hardware variants come along.
|
||||
*/
|
||||
if (ver.hw_variant != 0x0b && ver.hw_variant != 0x0c) {
|
||||
switch (ver.hw_variant) {
|
||||
case 0x0b: /* SfP */
|
||||
case 0x0c: /* WsP */
|
||||
case 0x12: /* ThP */
|
||||
break;
|
||||
default:
|
||||
BT_ERR("%s: Unsupported Intel hardware variant (%u)",
|
||||
hdev->name, ver.hw_variant);
|
||||
return -EINVAL;
|
||||
@ -2792,6 +2800,7 @@ static irqreturn_t btusb_oob_wake_handler(int irq, void *priv)
|
||||
struct btusb_data *data = priv;
|
||||
|
||||
pm_wakeup_event(&data->udev->dev, 0);
|
||||
pm_system_wakeup();
|
||||
|
||||
/* Disable only if not already disabled (keep it balanced) */
|
||||
if (test_and_clear_bit(BTUSB_OOB_WAKE_ENABLED, &data->flags)) {
|
||||
|
@ -146,13 +146,13 @@ static bool bcm_device_exists(struct bcm_device *device)
|
||||
static int bcm_gpio_set_power(struct bcm_device *dev, bool powered)
|
||||
{
|
||||
if (powered && !IS_ERR(dev->clk) && !dev->clk_enabled)
|
||||
clk_enable(dev->clk);
|
||||
clk_prepare_enable(dev->clk);
|
||||
|
||||
gpiod_set_value(dev->shutdown, powered);
|
||||
gpiod_set_value(dev->device_wakeup, powered);
|
||||
|
||||
if (!powered && !IS_ERR(dev->clk) && dev->clk_enabled)
|
||||
clk_disable(dev->clk);
|
||||
clk_disable_unprepare(dev->clk);
|
||||
|
||||
dev->clk_enabled = powered;
|
||||
|
||||
@ -287,6 +287,9 @@ static int bcm_open(struct hci_uart *hu)
|
||||
|
||||
hu->priv = bcm;
|
||||
|
||||
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);
|
||||
@ -307,7 +310,7 @@ static int bcm_open(struct hci_uart *hu)
|
||||
}
|
||||
|
||||
mutex_unlock(&bcm_device_lock);
|
||||
|
||||
out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -697,28 +700,14 @@ static int bcm_resource(struct acpi_resource *ares, void *data)
|
||||
/* Always tell the ACPI core to skip this resource */
|
||||
return 1;
|
||||
}
|
||||
#endif /* CONFIG_ACPI */
|
||||
|
||||
static int bcm_acpi_probe(struct bcm_device *dev)
|
||||
static int bcm_platform_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;
|
||||
int ret;
|
||||
|
||||
dev->name = dev_name(&pdev->dev);
|
||||
|
||||
/* Retrieve GPIO data */
|
||||
id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev);
|
||||
if (id)
|
||||
gpio_mapping = (const struct acpi_gpio_mapping *) id->driver_data;
|
||||
|
||||
ret = acpi_dev_add_driver_gpios(ACPI_COMPANION(&pdev->dev),
|
||||
gpio_mapping);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
|
||||
dev->device_wakeup = devm_gpiod_get_optional(&pdev->dev,
|
||||
@ -755,6 +744,33 @@ static int bcm_acpi_probe(struct bcm_device *dev)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
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;
|
||||
int ret;
|
||||
|
||||
/* Retrieve GPIO data */
|
||||
id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev);
|
||||
if (id)
|
||||
gpio_mapping = (const struct acpi_gpio_mapping *) id->driver_data;
|
||||
|
||||
ret = acpi_dev_add_driver_gpios(ACPI_COMPANION(&pdev->dev),
|
||||
gpio_mapping);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = bcm_platform_probe(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Retrieve UART ACPI info */
|
||||
ret = acpi_dev_get_resources(ACPI_COMPANION(&dev->pdev->dev),
|
||||
&resources, bcm_resource, dev);
|
||||
@ -789,7 +805,10 @@ static int bcm_probe(struct platform_device *pdev)
|
||||
|
||||
dev->pdev = pdev;
|
||||
|
||||
ret = bcm_acpi_probe(dev);
|
||||
if (has_acpi_companion(&pdev->dev))
|
||||
ret = bcm_acpi_probe(dev);
|
||||
else
|
||||
ret = bcm_platform_probe(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -171,9 +171,20 @@ struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb,
|
||||
const unsigned char *buffer, int count,
|
||||
const struct h4_recv_pkt *pkts, int pkts_count)
|
||||
{
|
||||
struct hci_uart *hu = hci_get_drvdata(hdev);
|
||||
u8 alignment = hu->alignment;
|
||||
|
||||
while (count) {
|
||||
int i, len;
|
||||
|
||||
/* remove padding bytes from buffer */
|
||||
for (; hu->padding && count > 0; hu->padding--) {
|
||||
count--;
|
||||
buffer++;
|
||||
}
|
||||
if (!count)
|
||||
break;
|
||||
|
||||
if (!skb) {
|
||||
for (i = 0; i < pkts_count; i++) {
|
||||
if (buffer[0] != (&pkts[i])->type)
|
||||
@ -253,11 +264,17 @@ struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb,
|
||||
}
|
||||
|
||||
if (!dlen) {
|
||||
hu->padding = (skb->len - 1) % alignment;
|
||||
hu->padding = (alignment - hu->padding) % alignment;
|
||||
|
||||
/* No more data, complete frame */
|
||||
(&pkts[i])->recv(hdev, skb);
|
||||
skb = NULL;
|
||||
}
|
||||
} else {
|
||||
hu->padding = (skb->len - 1) % alignment;
|
||||
hu->padding = (alignment - hu->padding) % alignment;
|
||||
|
||||
/* Complete frame */
|
||||
(&pkts[i])->recv(hdev, skb);
|
||||
skb = NULL;
|
||||
|
@ -307,6 +307,9 @@ static int intel_set_power(struct hci_uart *hu, bool powered)
|
||||
struct list_head *p;
|
||||
int err = -ENODEV;
|
||||
|
||||
if (!hu->tty->dev)
|
||||
return err;
|
||||
|
||||
mutex_lock(&intel_device_list_lock);
|
||||
|
||||
list_for_each(p, &intel_device_list) {
|
||||
@ -379,6 +382,9 @@ static void intel_busy_work(struct work_struct *work)
|
||||
struct intel_data *intel = container_of(work, struct intel_data,
|
||||
busy_work);
|
||||
|
||||
if (!intel->hu->tty->dev)
|
||||
return;
|
||||
|
||||
/* Link is busy, delay the suspend */
|
||||
mutex_lock(&intel_device_list_lock);
|
||||
list_for_each(p, &intel_device_list) {
|
||||
@ -601,12 +607,18 @@ static int intel_setup(struct hci_uart *hu)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* At the moment only the hardware variant iBT 3.0 (LnP/SfP) is
|
||||
* supported by this firmware loading method. This check has been
|
||||
* put in place to ensure correct forward compatibility options
|
||||
* when newer hardware variants come along.
|
||||
*/
|
||||
if (ver.hw_variant != 0x0b) {
|
||||
/* Check for supported iBT hardware variants of this firmware
|
||||
* loading method.
|
||||
*
|
||||
* This check has been put in place to ensure correct forward
|
||||
* compatibility options when newer hardware variants come along.
|
||||
*/
|
||||
switch (ver.hw_variant) {
|
||||
case 0x0b: /* LnP */
|
||||
case 0x0c: /* WsP */
|
||||
case 0x12: /* ThP */
|
||||
break;
|
||||
default:
|
||||
bt_dev_err(hdev, "Unsupported Intel hardware variant (%u)",
|
||||
ver.hw_variant);
|
||||
return -EINVAL;
|
||||
@ -699,11 +711,14 @@ static int intel_setup(struct hci_uart *hu)
|
||||
/* With this Intel bootloader only the hardware variant and device
|
||||
* revision information are used to select the right firmware.
|
||||
*
|
||||
* Currently this bootloader support is limited to hardware variant
|
||||
* iBT 3.0 (LnP/SfP) which is identified by the value 11 (0x0b).
|
||||
* The firmware filename is ibt-<hw_variant>-<dev_revid>.sfi.
|
||||
*
|
||||
* Currently the supported hardware variants are:
|
||||
* 11 (0x0b) for iBT 3.0 (LnP/SfP)
|
||||
*/
|
||||
snprintf(fwname, sizeof(fwname), "intel/ibt-11-%u.sfi",
|
||||
le16_to_cpu(params->dev_revid));
|
||||
snprintf(fwname, sizeof(fwname), "intel/ibt-%u-%u.sfi",
|
||||
le16_to_cpu(ver.hw_variant),
|
||||
le16_to_cpu(params->dev_revid));
|
||||
|
||||
err = request_firmware(&fw, fwname, &hdev->dev);
|
||||
if (err < 0) {
|
||||
@ -716,8 +731,9 @@ static int intel_setup(struct hci_uart *hu)
|
||||
bt_dev_info(hdev, "Found device firmware: %s", fwname);
|
||||
|
||||
/* Save the DDC file name for later */
|
||||
snprintf(fwname, sizeof(fwname), "intel/ibt-11-%u.ddc",
|
||||
le16_to_cpu(params->dev_revid));
|
||||
snprintf(fwname, sizeof(fwname), "intel/ibt-%u-%u.ddc",
|
||||
le16_to_cpu(ver.hw_variant),
|
||||
le16_to_cpu(params->dev_revid));
|
||||
|
||||
kfree_skb(skb);
|
||||
|
||||
@ -889,6 +905,8 @@ static int intel_setup(struct hci_uart *hu)
|
||||
list_for_each(p, &intel_device_list) {
|
||||
struct intel_device *dev = list_entry(p, struct intel_device,
|
||||
list);
|
||||
if (!hu->tty->dev)
|
||||
break;
|
||||
if (hu->tty->dev->parent == dev->pdev->dev.parent) {
|
||||
if (device_may_wakeup(&dev->pdev->dev)) {
|
||||
set_bit(STATE_LPM_ENABLED, &intel->flags);
|
||||
@ -1056,6 +1074,9 @@ static int intel_enqueue(struct hci_uart *hu, struct sk_buff *skb)
|
||||
|
||||
BT_DBG("hu %p skb %p", hu, skb);
|
||||
|
||||
if (!hu->tty->dev)
|
||||
goto out_enqueue;
|
||||
|
||||
/* Be sure our controller is resumed and potential LPM transaction
|
||||
* completed before enqueuing any packet.
|
||||
*/
|
||||
@ -1072,7 +1093,7 @@ static int intel_enqueue(struct hci_uart *hu, struct sk_buff *skb)
|
||||
}
|
||||
}
|
||||
mutex_unlock(&intel_device_list_lock);
|
||||
|
||||
out_enqueue:
|
||||
skb_queue_tail(&intel->txq, skb);
|
||||
|
||||
return 0;
|
||||
|
@ -134,6 +134,7 @@ int hci_uart_tx_wakeup(struct hci_uart *hu)
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hci_uart_tx_wakeup);
|
||||
|
||||
static void hci_uart_write_work(struct work_struct *work)
|
||||
{
|
||||
@ -318,25 +319,6 @@ void hci_uart_set_speeds(struct hci_uart *hu, unsigned int init_speed,
|
||||
hu->oper_speed = oper_speed;
|
||||
}
|
||||
|
||||
void hci_uart_init_tty(struct hci_uart *hu)
|
||||
{
|
||||
struct tty_struct *tty = hu->tty;
|
||||
struct ktermios ktermios;
|
||||
|
||||
/* Bring the UART into a known 8 bits no parity hw fc state */
|
||||
ktermios = tty->termios;
|
||||
ktermios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP |
|
||||
INLCR | IGNCR | ICRNL | IXON);
|
||||
ktermios.c_oflag &= ~OPOST;
|
||||
ktermios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
|
||||
ktermios.c_cflag &= ~(CSIZE | PARENB);
|
||||
ktermios.c_cflag |= CS8;
|
||||
ktermios.c_cflag |= CRTSCTS;
|
||||
|
||||
/* tty_set_termios() return not checked as it is always 0 */
|
||||
tty_set_termios(tty, &ktermios);
|
||||
}
|
||||
|
||||
void hci_uart_set_baudrate(struct hci_uart *hu, unsigned int speed)
|
||||
{
|
||||
struct tty_struct *tty = hu->tty;
|
||||
@ -459,6 +441,10 @@ static int hci_uart_tty_open(struct tty_struct *tty)
|
||||
hu->tty = tty;
|
||||
tty->receive_room = 65536;
|
||||
|
||||
/* disable alignment support by default */
|
||||
hu->alignment = 1;
|
||||
hu->padding = 0;
|
||||
|
||||
INIT_WORK(&hu->init_ready, hci_uart_init_work);
|
||||
INIT_WORK(&hu->write_work, hci_uart_write_work);
|
||||
|
||||
|
@ -34,20 +34,24 @@
|
||||
#include <linux/sched.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/poll.h>
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/serdev.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/ti_wilink_st.h>
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
|
||||
#include "hci_uart.h"
|
||||
|
||||
@ -76,6 +80,12 @@ struct hcill_cmd {
|
||||
u8 cmd;
|
||||
} __packed;
|
||||
|
||||
struct ll_device {
|
||||
struct hci_uart hu;
|
||||
struct serdev_device *serdev;
|
||||
struct gpio_desc *enable_gpio;
|
||||
};
|
||||
|
||||
struct ll_struct {
|
||||
unsigned long rx_state;
|
||||
unsigned long rx_count;
|
||||
@ -136,6 +146,9 @@ static int ll_open(struct hci_uart *hu)
|
||||
|
||||
hu->priv = ll;
|
||||
|
||||
if (hu->serdev)
|
||||
serdev_device_open(hu->serdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -164,6 +177,13 @@ static int ll_close(struct hci_uart *hu)
|
||||
|
||||
kfree_skb(ll->rx_skb);
|
||||
|
||||
if (hu->serdev) {
|
||||
struct ll_device *lldev = serdev_device_get_drvdata(hu->serdev);
|
||||
gpiod_set_value_cansleep(lldev->enable_gpio, 0);
|
||||
|
||||
serdev_device_close(hu->serdev);
|
||||
}
|
||||
|
||||
hu->priv = NULL;
|
||||
|
||||
kfree(ll);
|
||||
@ -505,9 +525,245 @@ static struct sk_buff *ll_dequeue(struct hci_uart *hu)
|
||||
return skb_dequeue(&ll->txq);
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_SERIAL_DEV_BUS)
|
||||
static int read_local_version(struct hci_dev *hdev)
|
||||
{
|
||||
int err = 0;
|
||||
unsigned short version = 0;
|
||||
struct sk_buff *skb;
|
||||
struct hci_rp_read_local_version *ver;
|
||||
|
||||
skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
bt_dev_err(hdev, "Reading TI version information failed (%ld)",
|
||||
PTR_ERR(skb));
|
||||
err = PTR_ERR(skb);
|
||||
goto out;
|
||||
}
|
||||
if (skb->len != sizeof(*ver)) {
|
||||
err = -EILSEQ;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ver = (struct hci_rp_read_local_version *)skb->data;
|
||||
if (le16_to_cpu(ver->manufacturer) != 13) {
|
||||
err = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
version = le16_to_cpu(ver->lmp_subver);
|
||||
|
||||
out:
|
||||
if (err) bt_dev_err(hdev, "Failed to read TI version info: %d", err);
|
||||
kfree_skb(skb);
|
||||
return err ? err : version;
|
||||
}
|
||||
|
||||
/**
|
||||
* download_firmware -
|
||||
* internal function which parses through the .bts firmware
|
||||
* script file intreprets SEND, DELAY actions only as of now
|
||||
*/
|
||||
static int download_firmware(struct ll_device *lldev)
|
||||
{
|
||||
unsigned short chip, min_ver, maj_ver;
|
||||
int version, err, len;
|
||||
unsigned char *ptr, *action_ptr;
|
||||
unsigned char bts_scr_name[40]; /* 40 char long bts scr name? */
|
||||
const struct firmware *fw;
|
||||
struct sk_buff *skb;
|
||||
struct hci_command *cmd;
|
||||
|
||||
version = read_local_version(lldev->hu.hdev);
|
||||
if (version < 0)
|
||||
return version;
|
||||
|
||||
chip = (version & 0x7C00) >> 10;
|
||||
min_ver = (version & 0x007F);
|
||||
maj_ver = (version & 0x0380) >> 7;
|
||||
if (version & 0x8000)
|
||||
maj_ver |= 0x0008;
|
||||
|
||||
snprintf(bts_scr_name, sizeof(bts_scr_name),
|
||||
"ti-connectivity/TIInit_%d.%d.%d.bts",
|
||||
chip, maj_ver, min_ver);
|
||||
|
||||
err = request_firmware(&fw, bts_scr_name, &lldev->serdev->dev);
|
||||
if (err || !fw->data || !fw->size) {
|
||||
bt_dev_err(lldev->hu.hdev, "request_firmware failed(errno %d) for %s",
|
||||
err, bts_scr_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
ptr = (void *)fw->data;
|
||||
len = fw->size;
|
||||
/* bts_header to remove out magic number and
|
||||
* version
|
||||
*/
|
||||
ptr += sizeof(struct bts_header);
|
||||
len -= sizeof(struct bts_header);
|
||||
|
||||
while (len > 0 && ptr) {
|
||||
bt_dev_dbg(lldev->hu.hdev, " action size %d, type %d ",
|
||||
((struct bts_action *)ptr)->size,
|
||||
((struct bts_action *)ptr)->type);
|
||||
|
||||
action_ptr = &(((struct bts_action *)ptr)->data[0]);
|
||||
|
||||
switch (((struct bts_action *)ptr)->type) {
|
||||
case ACTION_SEND_COMMAND: /* action send */
|
||||
bt_dev_dbg(lldev->hu.hdev, "S");
|
||||
cmd = (struct hci_command *)action_ptr;
|
||||
if (cmd->opcode == 0xff36) {
|
||||
/* ignore remote change
|
||||
* baud rate HCI VS command */
|
||||
bt_dev_warn(lldev->hu.hdev, "change remote baud rate command in firmware");
|
||||
break;
|
||||
}
|
||||
if (cmd->prefix != 1)
|
||||
bt_dev_dbg(lldev->hu.hdev, "command type %d\n", cmd->prefix);
|
||||
|
||||
skb = __hci_cmd_sync(lldev->hu.hdev, cmd->opcode, cmd->plen, &cmd->speed, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
bt_dev_err(lldev->hu.hdev, "send command failed\n");
|
||||
goto out_rel_fw;
|
||||
}
|
||||
kfree_skb(skb);
|
||||
break;
|
||||
case ACTION_WAIT_EVENT: /* wait */
|
||||
/* no need to wait as command was synchronous */
|
||||
bt_dev_dbg(lldev->hu.hdev, "W");
|
||||
break;
|
||||
case ACTION_DELAY: /* sleep */
|
||||
bt_dev_info(lldev->hu.hdev, "sleep command in scr");
|
||||
mdelay(((struct bts_action_delay *)action_ptr)->msec);
|
||||
break;
|
||||
}
|
||||
len -= (sizeof(struct bts_action) +
|
||||
((struct bts_action *)ptr)->size);
|
||||
ptr += sizeof(struct bts_action) +
|
||||
((struct bts_action *)ptr)->size;
|
||||
}
|
||||
|
||||
out_rel_fw:
|
||||
/* fw download complete */
|
||||
release_firmware(fw);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ll_setup(struct hci_uart *hu)
|
||||
{
|
||||
int err, retry = 3;
|
||||
struct ll_device *lldev;
|
||||
struct serdev_device *serdev = hu->serdev;
|
||||
u32 speed;
|
||||
|
||||
if (!serdev)
|
||||
return 0;
|
||||
|
||||
lldev = serdev_device_get_drvdata(serdev);
|
||||
|
||||
serdev_device_set_flow_control(serdev, true);
|
||||
|
||||
do {
|
||||
/* Configure BT_EN to HIGH state */
|
||||
gpiod_set_value_cansleep(lldev->enable_gpio, 0);
|
||||
msleep(5);
|
||||
gpiod_set_value_cansleep(lldev->enable_gpio, 1);
|
||||
msleep(100);
|
||||
|
||||
err = download_firmware(lldev);
|
||||
if (!err)
|
||||
break;
|
||||
|
||||
/* Toggle BT_EN and retry */
|
||||
bt_dev_err(hu->hdev, "download firmware failed, retrying...");
|
||||
} while (retry--);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Operational speed if any */
|
||||
if (hu->oper_speed)
|
||||
speed = hu->oper_speed;
|
||||
else if (hu->proto->oper_speed)
|
||||
speed = hu->proto->oper_speed;
|
||||
else
|
||||
speed = 0;
|
||||
|
||||
if (speed) {
|
||||
struct sk_buff *skb = __hci_cmd_sync(hu->hdev, 0xff36, sizeof(speed), &speed, HCI_INIT_TIMEOUT);
|
||||
if (!IS_ERR(skb)) {
|
||||
kfree_skb(skb);
|
||||
serdev_device_set_baudrate(serdev, speed);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct hci_uart_proto llp;
|
||||
|
||||
static int hci_ti_probe(struct serdev_device *serdev)
|
||||
{
|
||||
struct hci_uart *hu;
|
||||
struct ll_device *lldev;
|
||||
u32 max_speed = 3000000;
|
||||
|
||||
lldev = devm_kzalloc(&serdev->dev, sizeof(struct ll_device), GFP_KERNEL);
|
||||
if (!lldev)
|
||||
return -ENOMEM;
|
||||
hu = &lldev->hu;
|
||||
|
||||
serdev_device_set_drvdata(serdev, lldev);
|
||||
lldev->serdev = hu->serdev = serdev;
|
||||
|
||||
lldev->enable_gpio = devm_gpiod_get_optional(&serdev->dev, "enable", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(lldev->enable_gpio))
|
||||
return PTR_ERR(lldev->enable_gpio);
|
||||
|
||||
of_property_read_u32(serdev->dev.of_node, "max-speed", &max_speed);
|
||||
hci_uart_set_speeds(hu, 115200, max_speed);
|
||||
|
||||
return hci_uart_register_device(hu, &llp);
|
||||
}
|
||||
|
||||
static void hci_ti_remove(struct serdev_device *serdev)
|
||||
{
|
||||
struct ll_device *lldev = serdev_device_get_drvdata(serdev);
|
||||
struct hci_uart *hu = &lldev->hu;
|
||||
struct hci_dev *hdev = hu->hdev;
|
||||
|
||||
cancel_work_sync(&hu->write_work);
|
||||
|
||||
hci_unregister_dev(hdev);
|
||||
hci_free_dev(hdev);
|
||||
hu->proto->close(hu);
|
||||
}
|
||||
|
||||
static const struct of_device_id hci_ti_of_match[] = {
|
||||
{ .compatible = "ti,wl1831-st" },
|
||||
{ .compatible = "ti,wl1835-st" },
|
||||
{ .compatible = "ti,wl1837-st" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, hci_ti_of_match);
|
||||
|
||||
static struct serdev_device_driver hci_ti_drv = {
|
||||
.driver = {
|
||||
.name = "hci-ti",
|
||||
.of_match_table = of_match_ptr(hci_ti_of_match),
|
||||
},
|
||||
.probe = hci_ti_probe,
|
||||
.remove = hci_ti_remove,
|
||||
};
|
||||
#else
|
||||
#define ll_setup NULL
|
||||
#endif
|
||||
|
||||
static const struct hci_uart_proto llp = {
|
||||
.id = HCI_UART_LL,
|
||||
.name = "LL",
|
||||
.setup = ll_setup,
|
||||
.open = ll_open,
|
||||
.close = ll_close,
|
||||
.recv = ll_recv,
|
||||
@ -518,10 +774,14 @@ static const struct hci_uart_proto llp = {
|
||||
|
||||
int __init ll_init(void)
|
||||
{
|
||||
serdev_device_driver_register(&hci_ti_drv);
|
||||
|
||||
return hci_uart_register_proto(&llp);
|
||||
}
|
||||
|
||||
int __exit ll_deinit(void)
|
||||
{
|
||||
serdev_device_driver_unregister(&hci_ti_drv);
|
||||
|
||||
return hci_uart_unregister_proto(&llp);
|
||||
}
|
||||
|
820
drivers/bluetooth/hci_nokia.c
Normal file
820
drivers/bluetooth/hci_nokia.c
Normal file
@ -0,0 +1,820 @@
|
||||
/*
|
||||
* Bluetooth HCI UART H4 driver with Nokia Extensions AKA Nokia H4+
|
||||
*
|
||||
* Copyright (C) 2015 Marcel Holtmann <marcel@holtmann.org>
|
||||
* Copyright (C) 2015-2017 Sebastian Reichel <sre@kernel.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/serdev.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/unaligned/le_struct.h>
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
|
||||
#include "hci_uart.h"
|
||||
#include "btbcm.h"
|
||||
|
||||
#define NOKIA_ID_BCM2048 0x04
|
||||
#define NOKIA_ID_TI1271 0x31
|
||||
|
||||
#define FIRMWARE_BCM2048 "nokia/bcmfw.bin"
|
||||
#define FIRMWARE_TI1271 "nokia/ti1273.bin"
|
||||
|
||||
#define HCI_NOKIA_NEG_PKT 0x06
|
||||
#define HCI_NOKIA_ALIVE_PKT 0x07
|
||||
#define HCI_NOKIA_RADIO_PKT 0x08
|
||||
|
||||
#define HCI_NOKIA_NEG_HDR_SIZE 1
|
||||
#define HCI_NOKIA_MAX_NEG_SIZE 255
|
||||
#define HCI_NOKIA_ALIVE_HDR_SIZE 1
|
||||
#define HCI_NOKIA_MAX_ALIVE_SIZE 255
|
||||
#define HCI_NOKIA_RADIO_HDR_SIZE 2
|
||||
#define HCI_NOKIA_MAX_RADIO_SIZE 255
|
||||
|
||||
#define NOKIA_PROTO_PKT 0x44
|
||||
#define NOKIA_PROTO_BYTE 0x4c
|
||||
|
||||
#define NOKIA_NEG_REQ 0x00
|
||||
#define NOKIA_NEG_ACK 0x20
|
||||
#define NOKIA_NEG_NAK 0x40
|
||||
|
||||
#define H4_TYPE_SIZE 1
|
||||
|
||||
#define NOKIA_RECV_ALIVE \
|
||||
.type = HCI_NOKIA_ALIVE_PKT, \
|
||||
.hlen = HCI_NOKIA_ALIVE_HDR_SIZE, \
|
||||
.loff = 0, \
|
||||
.lsize = 1, \
|
||||
.maxlen = HCI_NOKIA_MAX_ALIVE_SIZE \
|
||||
|
||||
#define NOKIA_RECV_NEG \
|
||||
.type = HCI_NOKIA_NEG_PKT, \
|
||||
.hlen = HCI_NOKIA_NEG_HDR_SIZE, \
|
||||
.loff = 0, \
|
||||
.lsize = 1, \
|
||||
.maxlen = HCI_NOKIA_MAX_NEG_SIZE \
|
||||
|
||||
#define NOKIA_RECV_RADIO \
|
||||
.type = HCI_NOKIA_RADIO_PKT, \
|
||||
.hlen = HCI_NOKIA_RADIO_HDR_SIZE, \
|
||||
.loff = 1, \
|
||||
.lsize = 1, \
|
||||
.maxlen = HCI_NOKIA_MAX_RADIO_SIZE \
|
||||
|
||||
struct hci_nokia_neg_hdr {
|
||||
u8 dlen;
|
||||
} __packed;
|
||||
|
||||
struct hci_nokia_neg_cmd {
|
||||
u8 ack;
|
||||
u16 baud;
|
||||
u16 unused1;
|
||||
u8 proto;
|
||||
u16 sys_clk;
|
||||
u16 unused2;
|
||||
} __packed;
|
||||
|
||||
#define NOKIA_ALIVE_REQ 0x55
|
||||
#define NOKIA_ALIVE_RESP 0xcc
|
||||
|
||||
struct hci_nokia_alive_hdr {
|
||||
u8 dlen;
|
||||
} __packed;
|
||||
|
||||
struct hci_nokia_alive_pkt {
|
||||
u8 mid;
|
||||
u8 unused;
|
||||
} __packed;
|
||||
|
||||
struct hci_nokia_neg_evt {
|
||||
u8 ack;
|
||||
u16 baud;
|
||||
u16 unused1;
|
||||
u8 proto;
|
||||
u16 sys_clk;
|
||||
u16 unused2;
|
||||
u8 man_id;
|
||||
u8 ver_id;
|
||||
} __packed;
|
||||
|
||||
#define MAX_BAUD_RATE 3692300
|
||||
#define SETUP_BAUD_RATE 921600
|
||||
#define INIT_BAUD_RATE 120000
|
||||
|
||||
struct hci_nokia_radio_hdr {
|
||||
u8 evt;
|
||||
u8 dlen;
|
||||
} __packed;
|
||||
|
||||
struct nokia_bt_dev {
|
||||
struct hci_uart hu;
|
||||
struct serdev_device *serdev;
|
||||
|
||||
struct gpio_desc *reset;
|
||||
struct gpio_desc *wakeup_host;
|
||||
struct gpio_desc *wakeup_bt;
|
||||
unsigned long sysclk_speed;
|
||||
|
||||
int wake_irq;
|
||||
struct sk_buff *rx_skb;
|
||||
struct sk_buff_head txq;
|
||||
bdaddr_t bdaddr;
|
||||
|
||||
int init_error;
|
||||
struct completion init_completion;
|
||||
|
||||
u8 man_id;
|
||||
u8 ver_id;
|
||||
|
||||
bool initialized;
|
||||
bool tx_enabled;
|
||||
bool rx_enabled;
|
||||
};
|
||||
|
||||
static int nokia_enqueue(struct hci_uart *hu, struct sk_buff *skb);
|
||||
|
||||
static void nokia_flow_control(struct serdev_device *serdev, bool enable)
|
||||
{
|
||||
if (enable) {
|
||||
serdev_device_set_rts(serdev, true);
|
||||
serdev_device_set_flow_control(serdev, true);
|
||||
} else {
|
||||
serdev_device_set_flow_control(serdev, false);
|
||||
serdev_device_set_rts(serdev, false);
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t wakeup_handler(int irq, void *data)
|
||||
{
|
||||
struct nokia_bt_dev *btdev = data;
|
||||
struct device *dev = &btdev->serdev->dev;
|
||||
int wake_state = gpiod_get_value(btdev->wakeup_host);
|
||||
|
||||
if (btdev->rx_enabled == wake_state)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
if (wake_state)
|
||||
pm_runtime_get(dev);
|
||||
else
|
||||
pm_runtime_put(dev);
|
||||
|
||||
btdev->rx_enabled = wake_state;
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int nokia_reset(struct hci_uart *hu)
|
||||
{
|
||||
struct nokia_bt_dev *btdev = hu->priv;
|
||||
struct device *dev = &btdev->serdev->dev;
|
||||
int err;
|
||||
|
||||
/* reset routine */
|
||||
gpiod_set_value_cansleep(btdev->reset, 1);
|
||||
gpiod_set_value_cansleep(btdev->wakeup_bt, 1);
|
||||
|
||||
msleep(100);
|
||||
|
||||
/* safety check */
|
||||
err = gpiod_get_value_cansleep(btdev->wakeup_host);
|
||||
if (err == 1) {
|
||||
dev_err(dev, "reset: host wakeup not low!");
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
/* flush queue */
|
||||
serdev_device_write_flush(btdev->serdev);
|
||||
|
||||
/* init uart */
|
||||
nokia_flow_control(btdev->serdev, false);
|
||||
serdev_device_set_baudrate(btdev->serdev, INIT_BAUD_RATE);
|
||||
|
||||
gpiod_set_value_cansleep(btdev->reset, 0);
|
||||
|
||||
/* wait for cts */
|
||||
err = serdev_device_wait_for_cts(btdev->serdev, true, 200);
|
||||
if (err < 0) {
|
||||
dev_err(dev, "CTS not received: %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
nokia_flow_control(btdev->serdev, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nokia_send_alive_packet(struct hci_uart *hu)
|
||||
{
|
||||
struct nokia_bt_dev *btdev = hu->priv;
|
||||
struct device *dev = &btdev->serdev->dev;
|
||||
struct hci_nokia_alive_hdr *hdr;
|
||||
struct hci_nokia_alive_pkt *pkt;
|
||||
struct sk_buff *skb;
|
||||
int len;
|
||||
|
||||
init_completion(&btdev->init_completion);
|
||||
|
||||
len = H4_TYPE_SIZE + sizeof(*hdr) + sizeof(*pkt);
|
||||
skb = bt_skb_alloc(len, GFP_KERNEL);
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
hci_skb_pkt_type(skb) = HCI_NOKIA_ALIVE_PKT;
|
||||
memset(skb->data, 0x00, len);
|
||||
|
||||
hdr = (struct hci_nokia_alive_hdr *)skb_put(skb, sizeof(*hdr));
|
||||
hdr->dlen = sizeof(*pkt);
|
||||
pkt = (struct hci_nokia_alive_pkt *)skb_put(skb, sizeof(*pkt));
|
||||
pkt->mid = NOKIA_ALIVE_REQ;
|
||||
|
||||
nokia_enqueue(hu, skb);
|
||||
hci_uart_tx_wakeup(hu);
|
||||
|
||||
dev_dbg(dev, "Alive sent");
|
||||
|
||||
if (!wait_for_completion_interruptible_timeout(&btdev->init_completion,
|
||||
msecs_to_jiffies(1000))) {
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
if (btdev->init_error < 0)
|
||||
return btdev->init_error;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nokia_send_negotiation(struct hci_uart *hu)
|
||||
{
|
||||
struct nokia_bt_dev *btdev = hu->priv;
|
||||
struct device *dev = &btdev->serdev->dev;
|
||||
struct hci_nokia_neg_cmd *neg_cmd;
|
||||
struct hci_nokia_neg_hdr *neg_hdr;
|
||||
struct sk_buff *skb;
|
||||
int len, err;
|
||||
u16 baud = DIV_ROUND_CLOSEST(btdev->sysclk_speed * 10, SETUP_BAUD_RATE);
|
||||
int sysclk = btdev->sysclk_speed / 1000;
|
||||
|
||||
len = H4_TYPE_SIZE + sizeof(*neg_hdr) + sizeof(*neg_cmd);
|
||||
skb = bt_skb_alloc(len, GFP_KERNEL);
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
hci_skb_pkt_type(skb) = HCI_NOKIA_NEG_PKT;
|
||||
|
||||
neg_hdr = (struct hci_nokia_neg_hdr *)skb_put(skb, sizeof(*neg_hdr));
|
||||
neg_hdr->dlen = sizeof(*neg_cmd);
|
||||
|
||||
neg_cmd = (struct hci_nokia_neg_cmd *)skb_put(skb, sizeof(*neg_cmd));
|
||||
neg_cmd->ack = NOKIA_NEG_REQ;
|
||||
neg_cmd->baud = cpu_to_le16(baud);
|
||||
neg_cmd->unused1 = 0x0000;
|
||||
neg_cmd->proto = NOKIA_PROTO_BYTE;
|
||||
neg_cmd->sys_clk = cpu_to_le16(sysclk);
|
||||
neg_cmd->unused2 = 0x0000;
|
||||
|
||||
btdev->init_error = 0;
|
||||
init_completion(&btdev->init_completion);
|
||||
|
||||
nokia_enqueue(hu, skb);
|
||||
hci_uart_tx_wakeup(hu);
|
||||
|
||||
dev_dbg(dev, "Negotiation sent");
|
||||
|
||||
if (!wait_for_completion_interruptible_timeout(&btdev->init_completion,
|
||||
msecs_to_jiffies(10000))) {
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
if (btdev->init_error < 0)
|
||||
return btdev->init_error;
|
||||
|
||||
/* Change to previously negotiated speed. Flow Control
|
||||
* is disabled until bluetooth adapter is ready to avoid
|
||||
* broken bytes being received.
|
||||
*/
|
||||
nokia_flow_control(btdev->serdev, false);
|
||||
serdev_device_set_baudrate(btdev->serdev, SETUP_BAUD_RATE);
|
||||
err = serdev_device_wait_for_cts(btdev->serdev, true, 200);
|
||||
if (err < 0) {
|
||||
dev_err(dev, "CTS not received: %d", err);
|
||||
return err;
|
||||
}
|
||||
nokia_flow_control(btdev->serdev, true);
|
||||
|
||||
dev_dbg(dev, "Negotiation successful");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nokia_setup_fw(struct hci_uart *hu)
|
||||
{
|
||||
struct nokia_bt_dev *btdev = hu->priv;
|
||||
struct device *dev = &btdev->serdev->dev;
|
||||
const char *fwname;
|
||||
const struct firmware *fw;
|
||||
const u8 *fw_ptr;
|
||||
size_t fw_size;
|
||||
int err;
|
||||
|
||||
dev_dbg(dev, "setup firmware");
|
||||
|
||||
if (btdev->man_id == NOKIA_ID_BCM2048) {
|
||||
fwname = FIRMWARE_BCM2048;
|
||||
} else if (btdev->man_id == NOKIA_ID_TI1271) {
|
||||
fwname = FIRMWARE_TI1271;
|
||||
} else {
|
||||
dev_err(dev, "Unsupported bluetooth device!");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
err = request_firmware(&fw, fwname, dev);
|
||||
if (err < 0) {
|
||||
dev_err(dev, "%s: Failed to load Nokia firmware file (%d)",
|
||||
hu->hdev->name, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
fw_ptr = fw->data;
|
||||
fw_size = fw->size;
|
||||
|
||||
while (fw_size >= 4) {
|
||||
u16 pkt_size = get_unaligned_le16(fw_ptr);
|
||||
u8 pkt_type = fw_ptr[2];
|
||||
const struct hci_command_hdr *cmd;
|
||||
u16 opcode;
|
||||
struct sk_buff *skb;
|
||||
|
||||
switch (pkt_type) {
|
||||
case HCI_COMMAND_PKT:
|
||||
cmd = (struct hci_command_hdr *)(fw_ptr + 3);
|
||||
opcode = le16_to_cpu(cmd->opcode);
|
||||
|
||||
skb = __hci_cmd_sync(hu->hdev, opcode, cmd->plen,
|
||||
fw_ptr + 3 + HCI_COMMAND_HDR_SIZE,
|
||||
HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
err = PTR_ERR(skb);
|
||||
dev_err(dev, "%s: FW command %04x failed (%d)",
|
||||
hu->hdev->name, opcode, err);
|
||||
goto done;
|
||||
}
|
||||
kfree_skb(skb);
|
||||
break;
|
||||
case HCI_NOKIA_RADIO_PKT:
|
||||
case HCI_NOKIA_NEG_PKT:
|
||||
case HCI_NOKIA_ALIVE_PKT:
|
||||
break;
|
||||
}
|
||||
|
||||
fw_ptr += pkt_size + 2;
|
||||
fw_size -= pkt_size + 2;
|
||||
}
|
||||
|
||||
done:
|
||||
release_firmware(fw);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int nokia_setup(struct hci_uart *hu)
|
||||
{
|
||||
struct nokia_bt_dev *btdev = hu->priv;
|
||||
struct device *dev = &btdev->serdev->dev;
|
||||
int err;
|
||||
|
||||
btdev->initialized = false;
|
||||
|
||||
nokia_flow_control(btdev->serdev, false);
|
||||
|
||||
pm_runtime_get_sync(dev);
|
||||
|
||||
if (btdev->tx_enabled) {
|
||||
gpiod_set_value_cansleep(btdev->wakeup_bt, 0);
|
||||
pm_runtime_put(&btdev->serdev->dev);
|
||||
btdev->tx_enabled = false;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "protocol setup");
|
||||
|
||||
/* 0. reset connection */
|
||||
err = nokia_reset(hu);
|
||||
if (err < 0) {
|
||||
dev_err(dev, "Reset failed: %d", err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* 1. negotiate speed etc */
|
||||
err = nokia_send_negotiation(hu);
|
||||
if (err < 0) {
|
||||
dev_err(dev, "Negotiation failed: %d", err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* 2. verify correct setup using alive packet */
|
||||
err = nokia_send_alive_packet(hu);
|
||||
if (err < 0) {
|
||||
dev_err(dev, "Alive check failed: %d", err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* 3. send firmware */
|
||||
err = nokia_setup_fw(hu);
|
||||
if (err < 0) {
|
||||
dev_err(dev, "Could not setup FW: %d", err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
nokia_flow_control(btdev->serdev, false);
|
||||
serdev_device_set_baudrate(btdev->serdev, MAX_BAUD_RATE);
|
||||
nokia_flow_control(btdev->serdev, true);
|
||||
|
||||
if (btdev->man_id == NOKIA_ID_BCM2048) {
|
||||
hu->hdev->set_bdaddr = btbcm_set_bdaddr;
|
||||
set_bit(HCI_QUIRK_INVALID_BDADDR, &hu->hdev->quirks);
|
||||
dev_dbg(dev, "bcm2048 has invalid bluetooth address!");
|
||||
}
|
||||
|
||||
dev_dbg(dev, "protocol setup done!");
|
||||
|
||||
gpiod_set_value_cansleep(btdev->wakeup_bt, 0);
|
||||
pm_runtime_put(dev);
|
||||
btdev->tx_enabled = false;
|
||||
btdev->initialized = true;
|
||||
|
||||
return 0;
|
||||
out:
|
||||
pm_runtime_put(dev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int nokia_open(struct hci_uart *hu)
|
||||
{
|
||||
struct device *dev = &hu->serdev->dev;
|
||||
|
||||
dev_dbg(dev, "protocol open");
|
||||
|
||||
serdev_device_open(hu->serdev);
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nokia_flush(struct hci_uart *hu)
|
||||
{
|
||||
struct nokia_bt_dev *btdev = hu->priv;
|
||||
|
||||
dev_dbg(&btdev->serdev->dev, "flush device");
|
||||
|
||||
skb_queue_purge(&btdev->txq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nokia_close(struct hci_uart *hu)
|
||||
{
|
||||
struct nokia_bt_dev *btdev = hu->priv;
|
||||
struct device *dev = &btdev->serdev->dev;
|
||||
|
||||
dev_dbg(dev, "close device");
|
||||
|
||||
btdev->initialized = false;
|
||||
|
||||
skb_queue_purge(&btdev->txq);
|
||||
|
||||
kfree_skb(btdev->rx_skb);
|
||||
|
||||
/* disable module */
|
||||
gpiod_set_value(btdev->reset, 1);
|
||||
gpiod_set_value(btdev->wakeup_bt, 0);
|
||||
|
||||
pm_runtime_disable(&btdev->serdev->dev);
|
||||
serdev_device_close(btdev->serdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Enqueue frame for transmittion (padding, crc, etc) */
|
||||
static int nokia_enqueue(struct hci_uart *hu, struct sk_buff *skb)
|
||||
{
|
||||
struct nokia_bt_dev *btdev = hu->priv;
|
||||
int err;
|
||||
|
||||
/* Prepend skb with frame type */
|
||||
memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
|
||||
|
||||
/* Packets must be word aligned */
|
||||
if (skb->len % 2) {
|
||||
err = skb_pad(skb, 1);
|
||||
if (err)
|
||||
return err;
|
||||
*skb_put(skb, 1) = 0x00;
|
||||
}
|
||||
|
||||
skb_queue_tail(&btdev->txq, skb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nokia_recv_negotiation_packet(struct hci_dev *hdev,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_uart *hu = hci_get_drvdata(hdev);
|
||||
struct nokia_bt_dev *btdev = hu->priv;
|
||||
struct device *dev = &btdev->serdev->dev;
|
||||
struct hci_nokia_neg_hdr *hdr;
|
||||
struct hci_nokia_neg_evt *evt;
|
||||
int ret = 0;
|
||||
|
||||
hdr = (struct hci_nokia_neg_hdr *)skb->data;
|
||||
if (hdr->dlen != sizeof(*evt)) {
|
||||
btdev->init_error = -EIO;
|
||||
ret = -EIO;
|
||||
goto finish_neg;
|
||||
}
|
||||
|
||||
evt = (struct hci_nokia_neg_evt *)skb_pull(skb, sizeof(*hdr));
|
||||
|
||||
if (evt->ack != NOKIA_NEG_ACK) {
|
||||
dev_err(dev, "Negotiation received: wrong reply");
|
||||
btdev->init_error = -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto finish_neg;
|
||||
}
|
||||
|
||||
btdev->man_id = evt->man_id;
|
||||
btdev->ver_id = evt->ver_id;
|
||||
|
||||
dev_dbg(dev, "Negotiation received: baud=%u:clk=%u:manu=%u:vers=%u",
|
||||
evt->baud, evt->sys_clk, evt->man_id, evt->ver_id);
|
||||
|
||||
finish_neg:
|
||||
complete(&btdev->init_completion);
|
||||
kfree_skb(skb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nokia_recv_alive_packet(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_uart *hu = hci_get_drvdata(hdev);
|
||||
struct nokia_bt_dev *btdev = hu->priv;
|
||||
struct device *dev = &btdev->serdev->dev;
|
||||
struct hci_nokia_alive_hdr *hdr;
|
||||
struct hci_nokia_alive_pkt *pkt;
|
||||
int ret = 0;
|
||||
|
||||
hdr = (struct hci_nokia_alive_hdr *)skb->data;
|
||||
if (hdr->dlen != sizeof(*pkt)) {
|
||||
dev_err(dev, "Corrupted alive message");
|
||||
btdev->init_error = -EIO;
|
||||
ret = -EIO;
|
||||
goto finish_alive;
|
||||
}
|
||||
|
||||
pkt = (struct hci_nokia_alive_pkt *)skb_pull(skb, sizeof(*hdr));
|
||||
|
||||
if (pkt->mid != NOKIA_ALIVE_RESP) {
|
||||
dev_err(dev, "Alive received: invalid response: 0x%02x!",
|
||||
pkt->mid);
|
||||
btdev->init_error = -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto finish_alive;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "Alive received");
|
||||
|
||||
finish_alive:
|
||||
complete(&btdev->init_completion);
|
||||
kfree_skb(skb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nokia_recv_radio(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
/* Packets received on the dedicated radio channel are
|
||||
* HCI events and so feed them back into the core.
|
||||
*/
|
||||
hci_skb_pkt_type(skb) = HCI_EVENT_PKT;
|
||||
return hci_recv_frame(hdev, skb);
|
||||
}
|
||||
|
||||
/* Recv data */
|
||||
static const struct h4_recv_pkt nokia_recv_pkts[] = {
|
||||
{ H4_RECV_ACL, .recv = hci_recv_frame },
|
||||
{ H4_RECV_SCO, .recv = hci_recv_frame },
|
||||
{ H4_RECV_EVENT, .recv = hci_recv_frame },
|
||||
{ NOKIA_RECV_ALIVE, .recv = nokia_recv_alive_packet },
|
||||
{ NOKIA_RECV_NEG, .recv = nokia_recv_negotiation_packet },
|
||||
{ NOKIA_RECV_RADIO, .recv = nokia_recv_radio },
|
||||
};
|
||||
|
||||
static int nokia_recv(struct hci_uart *hu, const void *data, int count)
|
||||
{
|
||||
struct nokia_bt_dev *btdev = hu->priv;
|
||||
struct device *dev = &btdev->serdev->dev;
|
||||
int err;
|
||||
|
||||
if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
|
||||
return -EUNATCH;
|
||||
|
||||
btdev->rx_skb = h4_recv_buf(hu->hdev, btdev->rx_skb, data, count,
|
||||
nokia_recv_pkts, ARRAY_SIZE(nokia_recv_pkts));
|
||||
if (IS_ERR(btdev->rx_skb)) {
|
||||
err = PTR_ERR(btdev->rx_skb);
|
||||
dev_err(dev, "Frame reassembly failed (%d)", err);
|
||||
btdev->rx_skb = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct sk_buff *nokia_dequeue(struct hci_uart *hu)
|
||||
{
|
||||
struct nokia_bt_dev *btdev = hu->priv;
|
||||
struct device *dev = &btdev->serdev->dev;
|
||||
struct sk_buff *result = skb_dequeue(&btdev->txq);
|
||||
|
||||
if (!btdev->initialized)
|
||||
return result;
|
||||
|
||||
if (btdev->tx_enabled == !!result)
|
||||
return result;
|
||||
|
||||
if (result) {
|
||||
pm_runtime_get_sync(dev);
|
||||
gpiod_set_value_cansleep(btdev->wakeup_bt, 1);
|
||||
} else {
|
||||
serdev_device_wait_until_sent(btdev->serdev, 0);
|
||||
gpiod_set_value_cansleep(btdev->wakeup_bt, 0);
|
||||
pm_runtime_put(dev);
|
||||
}
|
||||
|
||||
btdev->tx_enabled = !!result;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static const struct hci_uart_proto nokia_proto = {
|
||||
.id = HCI_UART_NOKIA,
|
||||
.name = "Nokia",
|
||||
.open = nokia_open,
|
||||
.close = nokia_close,
|
||||
.recv = nokia_recv,
|
||||
.enqueue = nokia_enqueue,
|
||||
.dequeue = nokia_dequeue,
|
||||
.flush = nokia_flush,
|
||||
.setup = nokia_setup,
|
||||
.manufacturer = 1,
|
||||
};
|
||||
|
||||
static int nokia_bluetooth_serdev_probe(struct serdev_device *serdev)
|
||||
{
|
||||
struct device *dev = &serdev->dev;
|
||||
struct nokia_bt_dev *btdev;
|
||||
struct clk *sysclk;
|
||||
int err = 0;
|
||||
|
||||
btdev = devm_kzalloc(dev, sizeof(*btdev), GFP_KERNEL);
|
||||
if (!btdev)
|
||||
return -ENOMEM;
|
||||
|
||||
btdev->hu.serdev = btdev->serdev = serdev;
|
||||
serdev_device_set_drvdata(serdev, btdev);
|
||||
|
||||
btdev->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(btdev->reset)) {
|
||||
err = PTR_ERR(btdev->reset);
|
||||
dev_err(dev, "could not get reset gpio: %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
btdev->wakeup_host = devm_gpiod_get(dev, "host-wakeup", GPIOD_IN);
|
||||
if (IS_ERR(btdev->wakeup_host)) {
|
||||
err = PTR_ERR(btdev->wakeup_host);
|
||||
dev_err(dev, "could not get host wakeup gpio: %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
btdev->wake_irq = gpiod_to_irq(btdev->wakeup_host);
|
||||
|
||||
err = devm_request_threaded_irq(dev, btdev->wake_irq, NULL,
|
||||
wakeup_handler,
|
||||
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
"wakeup", btdev);
|
||||
if (err) {
|
||||
dev_err(dev, "could request wakeup irq: %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
btdev->wakeup_bt = devm_gpiod_get(dev, "bluetooth-wakeup",
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR(btdev->wakeup_bt)) {
|
||||
err = PTR_ERR(btdev->wakeup_bt);
|
||||
dev_err(dev, "could not get BT wakeup gpio: %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
sysclk = devm_clk_get(dev, "sysclk");
|
||||
if (IS_ERR(sysclk)) {
|
||||
err = PTR_ERR(sysclk);
|
||||
dev_err(dev, "could not get sysclk: %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
clk_prepare_enable(sysclk);
|
||||
btdev->sysclk_speed = clk_get_rate(sysclk);
|
||||
clk_disable_unprepare(sysclk);
|
||||
|
||||
skb_queue_head_init(&btdev->txq);
|
||||
|
||||
btdev->hu.priv = btdev;
|
||||
btdev->hu.alignment = 2; /* Nokia H4+ is word aligned */
|
||||
|
||||
err = hci_uart_register_device(&btdev->hu, &nokia_proto);
|
||||
if (err) {
|
||||
dev_err(dev, "could not register bluetooth uart: %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nokia_bluetooth_serdev_remove(struct serdev_device *serdev)
|
||||
{
|
||||
struct nokia_bt_dev *btdev = serdev_device_get_drvdata(serdev);
|
||||
struct hci_uart *hu = &btdev->hu;
|
||||
struct hci_dev *hdev = hu->hdev;
|
||||
|
||||
cancel_work_sync(&hu->write_work);
|
||||
|
||||
hci_unregister_dev(hdev);
|
||||
hci_free_dev(hdev);
|
||||
hu->proto->close(hu);
|
||||
|
||||
pm_runtime_disable(&btdev->serdev->dev);
|
||||
}
|
||||
|
||||
static int nokia_bluetooth_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct serdev_device *serdev = to_serdev_device(dev);
|
||||
|
||||
nokia_flow_control(serdev, false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nokia_bluetooth_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct serdev_device *serdev = to_serdev_device(dev);
|
||||
|
||||
nokia_flow_control(serdev, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops nokia_bluetooth_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(nokia_bluetooth_runtime_suspend,
|
||||
nokia_bluetooth_runtime_resume,
|
||||
NULL)
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id nokia_bluetooth_of_match[] = {
|
||||
{ .compatible = "nokia,h4p-bluetooth", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, nokia_bluetooth_of_match);
|
||||
#endif
|
||||
|
||||
static struct serdev_device_driver nokia_bluetooth_serdev_driver = {
|
||||
.probe = nokia_bluetooth_serdev_probe,
|
||||
.remove = nokia_bluetooth_serdev_remove,
|
||||
.driver = {
|
||||
.name = "nokia-bluetooth",
|
||||
.pm = &nokia_bluetooth_pm_ops,
|
||||
.of_match_table = of_match_ptr(nokia_bluetooth_of_match),
|
||||
},
|
||||
};
|
||||
|
||||
module_serdev_device_driver(nokia_bluetooth_serdev_driver);
|
356
drivers/bluetooth/hci_serdev.c
Normal file
356
drivers/bluetooth/hci_serdev.c
Normal file
@ -0,0 +1,356 @@
|
||||
/*
|
||||
* Bluetooth HCI serdev driver lib
|
||||
*
|
||||
* Copyright (C) 2017 Linaro, Ltd., Rob Herring <robh@kernel.org>
|
||||
*
|
||||
* Based on hci_ldisc.c:
|
||||
*
|
||||
* Copyright (C) 2000-2001 Qualcomm Incorporated
|
||||
* Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
|
||||
* Copyright (C) 2004-2005 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 as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/serdev.h>
|
||||
#include <linux/skbuff.h>
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
|
||||
#include "hci_uart.h"
|
||||
|
||||
struct serdev_device_ops hci_serdev_client_ops;
|
||||
|
||||
static inline void hci_uart_tx_complete(struct hci_uart *hu, int pkt_type)
|
||||
{
|
||||
struct hci_dev *hdev = hu->hdev;
|
||||
|
||||
/* Update HCI stat counters */
|
||||
switch (pkt_type) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
static inline struct sk_buff *hci_uart_dequeue(struct hci_uart *hu)
|
||||
{
|
||||
struct sk_buff *skb = hu->tx_skb;
|
||||
|
||||
if (!skb)
|
||||
skb = hu->proto->dequeue(hu);
|
||||
else
|
||||
hu->tx_skb = NULL;
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
||||
static void hci_uart_write_work(struct work_struct *work)
|
||||
{
|
||||
struct hci_uart *hu = container_of(work, struct hci_uart, write_work);
|
||||
struct serdev_device *serdev = hu->serdev;
|
||||
struct hci_dev *hdev = hu->hdev;
|
||||
struct sk_buff *skb;
|
||||
|
||||
/* REVISIT:
|
||||
* should we cope with bad skbs or ->write() returning an error value?
|
||||
*/
|
||||
do {
|
||||
clear_bit(HCI_UART_TX_WAKEUP, &hu->tx_state);
|
||||
|
||||
while ((skb = hci_uart_dequeue(hu))) {
|
||||
int len;
|
||||
|
||||
len = serdev_device_write_buf(serdev,
|
||||
skb->data, skb->len);
|
||||
hdev->stat.byte_tx += len;
|
||||
|
||||
skb_pull(skb, len);
|
||||
if (skb->len) {
|
||||
hu->tx_skb = skb;
|
||||
break;
|
||||
}
|
||||
|
||||
hci_uart_tx_complete(hu, hci_skb_pkt_type(skb));
|
||||
kfree_skb(skb);
|
||||
}
|
||||
} while(test_bit(HCI_UART_TX_WAKEUP, &hu->tx_state));
|
||||
|
||||
clear_bit(HCI_UART_SENDING, &hu->tx_state);
|
||||
}
|
||||
|
||||
/* ------- Interface to HCI layer ------ */
|
||||
|
||||
/* Initialize device */
|
||||
static int hci_uart_open(struct hci_dev *hdev)
|
||||
{
|
||||
BT_DBG("%s %p", hdev->name, hdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Reset device */
|
||||
static int hci_uart_flush(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_uart *hu = hci_get_drvdata(hdev);
|
||||
|
||||
BT_DBG("hdev %p serdev %p", hdev, hu->serdev);
|
||||
|
||||
if (hu->tx_skb) {
|
||||
kfree_skb(hu->tx_skb); hu->tx_skb = NULL;
|
||||
}
|
||||
|
||||
/* Flush any pending characters in the driver and discipline. */
|
||||
serdev_device_write_flush(hu->serdev);
|
||||
|
||||
if (test_bit(HCI_UART_PROTO_READY, &hu->flags))
|
||||
hu->proto->flush(hu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Close device */
|
||||
static int hci_uart_close(struct hci_dev *hdev)
|
||||
{
|
||||
BT_DBG("hdev %p", hdev);
|
||||
|
||||
hci_uart_flush(hdev);
|
||||
hdev->flush = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Send frames from HCI layer */
|
||||
static int hci_uart_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_uart *hu = hci_get_drvdata(hdev);
|
||||
|
||||
BT_DBG("%s: type %d len %d", hdev->name, hci_skb_pkt_type(skb),
|
||||
skb->len);
|
||||
|
||||
hu->proto->enqueue(hu, skb);
|
||||
|
||||
hci_uart_tx_wakeup(hu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hci_uart_setup(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_uart *hu = hci_get_drvdata(hdev);
|
||||
struct hci_rp_read_local_version *ver;
|
||||
struct sk_buff *skb;
|
||||
unsigned int speed;
|
||||
int err;
|
||||
|
||||
/* Init speed if any */
|
||||
if (hu->init_speed)
|
||||
speed = hu->init_speed;
|
||||
else if (hu->proto->init_speed)
|
||||
speed = hu->proto->init_speed;
|
||||
else
|
||||
speed = 0;
|
||||
|
||||
if (speed)
|
||||
serdev_device_set_baudrate(hu->serdev, speed);
|
||||
|
||||
/* Operational speed if any */
|
||||
if (hu->oper_speed)
|
||||
speed = hu->oper_speed;
|
||||
else if (hu->proto->oper_speed)
|
||||
speed = hu->proto->oper_speed;
|
||||
else
|
||||
speed = 0;
|
||||
|
||||
if (hu->proto->set_baudrate && speed) {
|
||||
err = hu->proto->set_baudrate(hu, speed);
|
||||
if (err)
|
||||
BT_ERR("%s: failed to set baudrate", hdev->name);
|
||||
else
|
||||
serdev_device_set_baudrate(hu->serdev, speed);
|
||||
}
|
||||
|
||||
if (hu->proto->setup)
|
||||
return hu->proto->setup(hu);
|
||||
|
||||
if (!test_bit(HCI_UART_VND_DETECT, &hu->hdev_flags))
|
||||
return 0;
|
||||
|
||||
skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL,
|
||||
HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
BT_ERR("%s: Reading local version information failed (%ld)",
|
||||
hdev->name, PTR_ERR(skb));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (skb->len != sizeof(*ver)) {
|
||||
BT_ERR("%s: Event length mismatch for version information",
|
||||
hdev->name);
|
||||
}
|
||||
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** hci_uart_write_wakeup - transmit buffer wakeup
|
||||
* @serdev: serial device
|
||||
*
|
||||
* This function is called by the serdev framework when it accepts
|
||||
* more data being sent.
|
||||
*/
|
||||
static void hci_uart_write_wakeup(struct serdev_device *serdev)
|
||||
{
|
||||
struct hci_uart *hu = serdev_device_get_drvdata(serdev);
|
||||
|
||||
BT_DBG("");
|
||||
|
||||
if (!hu || serdev != hu->serdev) {
|
||||
WARN_ON(1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (test_bit(HCI_UART_PROTO_READY, &hu->flags))
|
||||
hci_uart_tx_wakeup(hu);
|
||||
}
|
||||
|
||||
/** hci_uart_receive_buf - receive buffer wakeup
|
||||
* @serdev: serial device
|
||||
* @data: pointer to received data
|
||||
* @count: count of received data in bytes
|
||||
*
|
||||
* This function is called by the serdev framework when it received data
|
||||
* in the RX buffer.
|
||||
*
|
||||
* Return: number of processed bytes
|
||||
*/
|
||||
static int hci_uart_receive_buf(struct serdev_device *serdev, const u8 *data,
|
||||
size_t count)
|
||||
{
|
||||
struct hci_uart *hu = serdev_device_get_drvdata(serdev);
|
||||
|
||||
if (!hu || serdev != hu->serdev) {
|
||||
WARN_ON(1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!test_bit(HCI_UART_PROTO_READY, &hu->flags))
|
||||
return 0;
|
||||
|
||||
/* It does not need a lock here as it is already protected by a mutex in
|
||||
* tty caller
|
||||
*/
|
||||
hu->proto->recv(hu, data, count);
|
||||
|
||||
if (hu->hdev)
|
||||
hu->hdev->stat.byte_rx += count;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
struct serdev_device_ops hci_serdev_client_ops = {
|
||||
.receive_buf = hci_uart_receive_buf,
|
||||
.write_wakeup = hci_uart_write_wakeup,
|
||||
};
|
||||
|
||||
int hci_uart_register_device(struct hci_uart *hu,
|
||||
const struct hci_uart_proto *p)
|
||||
{
|
||||
int err;
|
||||
struct hci_dev *hdev;
|
||||
|
||||
BT_DBG("");
|
||||
|
||||
serdev_device_set_client_ops(hu->serdev, &hci_serdev_client_ops);
|
||||
|
||||
err = p->open(hu);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
hu->proto = p;
|
||||
set_bit(HCI_UART_PROTO_READY, &hu->flags);
|
||||
|
||||
/* Initialize and register HCI device */
|
||||
hdev = hci_alloc_dev();
|
||||
if (!hdev) {
|
||||
BT_ERR("Can't allocate HCI device");
|
||||
err = -ENOMEM;
|
||||
goto err_alloc;
|
||||
}
|
||||
|
||||
hu->hdev = hdev;
|
||||
|
||||
hdev->bus = HCI_UART;
|
||||
hci_set_drvdata(hdev, hu);
|
||||
|
||||
INIT_WORK(&hu->write_work, hci_uart_write_work);
|
||||
|
||||
/* Only when vendor specific setup callback is provided, consider
|
||||
* the manufacturer information valid. This avoids filling in the
|
||||
* value for Ericsson when nothing is specified.
|
||||
*/
|
||||
if (hu->proto->setup)
|
||||
hdev->manufacturer = hu->proto->manufacturer;
|
||||
|
||||
hdev->open = hci_uart_open;
|
||||
hdev->close = hci_uart_close;
|
||||
hdev->flush = hci_uart_flush;
|
||||
hdev->send = hci_uart_send_frame;
|
||||
hdev->setup = hci_uart_setup;
|
||||
SET_HCIDEV_DEV(hdev, &hu->serdev->dev);
|
||||
|
||||
if (test_bit(HCI_UART_RAW_DEVICE, &hu->hdev_flags))
|
||||
set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks);
|
||||
|
||||
if (test_bit(HCI_UART_EXT_CONFIG, &hu->hdev_flags))
|
||||
set_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks);
|
||||
|
||||
if (!test_bit(HCI_UART_RESET_ON_INIT, &hu->hdev_flags))
|
||||
set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
|
||||
|
||||
if (test_bit(HCI_UART_CREATE_AMP, &hu->hdev_flags))
|
||||
hdev->dev_type = HCI_AMP;
|
||||
else
|
||||
hdev->dev_type = HCI_PRIMARY;
|
||||
|
||||
if (test_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags))
|
||||
return 0;
|
||||
|
||||
if (hci_register_dev(hdev) < 0) {
|
||||
BT_ERR("Can't register HCI device");
|
||||
err = -ENODEV;
|
||||
goto err_register;
|
||||
}
|
||||
|
||||
set_bit(HCI_UART_REGISTERED, &hu->flags);
|
||||
|
||||
return 0;
|
||||
|
||||
err_register:
|
||||
hci_free_dev(hdev);
|
||||
err_alloc:
|
||||
clear_bit(HCI_UART_PROTO_READY, &hu->flags);
|
||||
p->close(hu);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hci_uart_register_device);
|
@ -58,6 +58,7 @@
|
||||
#define HCI_UART_VND_DETECT 5
|
||||
|
||||
struct hci_uart;
|
||||
struct serdev_device;
|
||||
|
||||
struct hci_uart_proto {
|
||||
unsigned int id;
|
||||
@ -77,6 +78,7 @@ struct hci_uart_proto {
|
||||
|
||||
struct hci_uart {
|
||||
struct tty_struct *tty;
|
||||
struct serdev_device *serdev;
|
||||
struct hci_dev *hdev;
|
||||
unsigned long flags;
|
||||
unsigned long hdev_flags;
|
||||
@ -92,6 +94,9 @@ struct hci_uart {
|
||||
|
||||
unsigned int init_speed;
|
||||
unsigned int oper_speed;
|
||||
|
||||
u8 alignment;
|
||||
u8 padding;
|
||||
};
|
||||
|
||||
/* HCI_UART proto flag bits */
|
||||
@ -105,9 +110,10 @@ struct hci_uart {
|
||||
|
||||
int hci_uart_register_proto(const struct hci_uart_proto *p);
|
||||
int hci_uart_unregister_proto(const struct hci_uart_proto *p);
|
||||
int hci_uart_register_device(struct hci_uart *hu, const struct hci_uart_proto *p);
|
||||
|
||||
int hci_uart_tx_wakeup(struct hci_uart *hu);
|
||||
int hci_uart_init_ready(struct hci_uart *hu);
|
||||
void hci_uart_init_tty(struct hci_uart *hu);
|
||||
void hci_uart_set_baudrate(struct hci_uart *hu, unsigned int speed);
|
||||
void hci_uart_set_flow_control(struct hci_uart *hu, bool enable);
|
||||
void hci_uart_set_speeds(struct hci_uart *hu, unsigned int init_speed,
|
||||
|
@ -82,3 +82,25 @@ config IEEE802154_ADF7242
|
||||
|
||||
This driver can also be built as a module. To do so, say M here.
|
||||
the module will be called 'adf7242'.
|
||||
|
||||
config IEEE802154_CA8210
|
||||
tristate "Cascoda CA8210 transceiver driver"
|
||||
depends on IEEE802154_DRIVERS && MAC802154
|
||||
depends on SPI
|
||||
select COMMON_CLK
|
||||
---help---
|
||||
Say Y here to enable the CA8210 SPI 802.15.4 wireless
|
||||
controller.
|
||||
|
||||
This driver can also be built as a module. To do so, say M here.
|
||||
the module will be called 'ca8210'.
|
||||
|
||||
config IEEE802154_CA8210_DEBUGFS
|
||||
bool "CA8210 debugfs interface"
|
||||
depends on IEEE802154_CA8210
|
||||
depends on DEBUG_FS
|
||||
---help---
|
||||
This option compiles debugfs code for the ca8210 driver. This
|
||||
exposes a debugfs node for each CA8210 instance which allows
|
||||
direct use of the Cascoda API, exposing the 802.15.4 MAC
|
||||
management entities.
|
||||
|
@ -4,3 +4,4 @@ obj-$(CONFIG_IEEE802154_MRF24J40) += mrf24j40.o
|
||||
obj-$(CONFIG_IEEE802154_CC2520) += cc2520.o
|
||||
obj-$(CONFIG_IEEE802154_ATUSB) += atusb.o
|
||||
obj-$(CONFIG_IEEE802154_ADF7242) += adf7242.o
|
||||
obj-$(CONFIG_IEEE802154_CA8210) += ca8210.o
|
||||
|
3242
drivers/net/ieee802154/ca8210.c
Normal file
3242
drivers/net/ieee802154/ca8210.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -173,6 +173,39 @@ void serdev_device_set_flow_control(struct serdev_device *serdev, bool enable)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(serdev_device_set_flow_control);
|
||||
|
||||
void serdev_device_wait_until_sent(struct serdev_device *serdev, long timeout)
|
||||
{
|
||||
struct serdev_controller *ctrl = serdev->ctrl;
|
||||
|
||||
if (!ctrl || !ctrl->ops->wait_until_sent)
|
||||
return;
|
||||
|
||||
ctrl->ops->wait_until_sent(ctrl, timeout);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(serdev_device_wait_until_sent);
|
||||
|
||||
int serdev_device_get_tiocm(struct serdev_device *serdev)
|
||||
{
|
||||
struct serdev_controller *ctrl = serdev->ctrl;
|
||||
|
||||
if (!ctrl || !ctrl->ops->get_tiocm)
|
||||
return -ENOTSUPP;
|
||||
|
||||
return ctrl->ops->get_tiocm(ctrl);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(serdev_device_get_tiocm);
|
||||
|
||||
int serdev_device_set_tiocm(struct serdev_device *serdev, int set, int clear)
|
||||
{
|
||||
struct serdev_controller *ctrl = serdev->ctrl;
|
||||
|
||||
if (!ctrl || !ctrl->ops->set_tiocm)
|
||||
return -ENOTSUPP;
|
||||
|
||||
return ctrl->ops->set_tiocm(ctrl, set, clear);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(serdev_device_set_tiocm);
|
||||
|
||||
static int serdev_drv_probe(struct device *dev)
|
||||
{
|
||||
const struct serdev_device_driver *sdrv = to_serdev_device_driver(dev->driver);
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <linux/serdev.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/tty_driver.h>
|
||||
#include <linux/poll.h>
|
||||
|
||||
#define SERPORT_ACTIVE 1
|
||||
|
||||
@ -46,11 +47,11 @@ static void ttyport_write_wakeup(struct tty_port *port)
|
||||
struct serdev_controller *ctrl = port->client_data;
|
||||
struct serport *serport = serdev_controller_get_drvdata(ctrl);
|
||||
|
||||
if (!test_and_clear_bit(TTY_DO_WRITE_WAKEUP, &port->tty->flags))
|
||||
return;
|
||||
|
||||
if (test_bit(SERPORT_ACTIVE, &serport->flags))
|
||||
if (test_and_clear_bit(TTY_DO_WRITE_WAKEUP, &port->tty->flags) &&
|
||||
test_bit(SERPORT_ACTIVE, &serport->flags))
|
||||
serdev_controller_write_wakeup(ctrl);
|
||||
|
||||
wake_up_interruptible_poll(&port->tty->write_wait, POLLOUT);
|
||||
}
|
||||
|
||||
static const struct tty_port_client_operations client_ops = {
|
||||
@ -167,6 +168,36 @@ static void ttyport_set_flow_control(struct serdev_controller *ctrl, bool enable
|
||||
tty_set_termios(tty, &ktermios);
|
||||
}
|
||||
|
||||
static void ttyport_wait_until_sent(struct serdev_controller *ctrl, long timeout)
|
||||
{
|
||||
struct serport *serport = serdev_controller_get_drvdata(ctrl);
|
||||
struct tty_struct *tty = serport->tty;
|
||||
|
||||
tty_wait_until_sent(tty, timeout);
|
||||
}
|
||||
|
||||
static int ttyport_get_tiocm(struct serdev_controller *ctrl)
|
||||
{
|
||||
struct serport *serport = serdev_controller_get_drvdata(ctrl);
|
||||
struct tty_struct *tty = serport->tty;
|
||||
|
||||
if (!tty->ops->tiocmget)
|
||||
return -ENOTSUPP;
|
||||
|
||||
return tty->driver->ops->tiocmget(tty);
|
||||
}
|
||||
|
||||
static int ttyport_set_tiocm(struct serdev_controller *ctrl, unsigned int set, unsigned int clear)
|
||||
{
|
||||
struct serport *serport = serdev_controller_get_drvdata(ctrl);
|
||||
struct tty_struct *tty = serport->tty;
|
||||
|
||||
if (!tty->ops->tiocmset)
|
||||
return -ENOTSUPP;
|
||||
|
||||
return tty->driver->ops->tiocmset(tty, set, clear);
|
||||
}
|
||||
|
||||
static const struct serdev_controller_ops ctrl_ops = {
|
||||
.write_buf = ttyport_write_buf,
|
||||
.write_flush = ttyport_write_flush,
|
||||
@ -175,6 +206,9 @@ static const struct serdev_controller_ops ctrl_ops = {
|
||||
.close = ttyport_close,
|
||||
.set_flow_control = ttyport_set_flow_control,
|
||||
.set_baudrate = ttyport_set_baudrate,
|
||||
.wait_until_sent = ttyport_wait_until_sent,
|
||||
.get_tiocm = ttyport_get_tiocm,
|
||||
.set_tiocm = ttyport_set_tiocm,
|
||||
};
|
||||
|
||||
struct device *serdev_tty_port_register(struct tty_port *port,
|
||||
|
@ -1597,6 +1597,9 @@ static struct omap_uart_port_info *of_get_uart_port_info(struct device *dev)
|
||||
|
||||
of_property_read_u32(dev->of_node, "clock-frequency",
|
||||
&omap_up_info->uartclk);
|
||||
|
||||
omap_up_info->flags = UPF_BOOT_AUTOCONF;
|
||||
|
||||
return omap_up_info;
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,8 @@
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/termios.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
struct serdev_controller;
|
||||
struct serdev_device;
|
||||
@ -81,6 +83,9 @@ struct serdev_controller_ops {
|
||||
void (*close)(struct serdev_controller *);
|
||||
void (*set_flow_control)(struct serdev_controller *, bool);
|
||||
unsigned int (*set_baudrate)(struct serdev_controller *, unsigned int);
|
||||
void (*wait_until_sent)(struct serdev_controller *, long);
|
||||
int (*get_tiocm)(struct serdev_controller *);
|
||||
int (*set_tiocm)(struct serdev_controller *, unsigned int, unsigned int);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -186,6 +191,9 @@ int serdev_device_open(struct serdev_device *);
|
||||
void serdev_device_close(struct serdev_device *);
|
||||
unsigned int serdev_device_set_baudrate(struct serdev_device *, unsigned int);
|
||||
void serdev_device_set_flow_control(struct serdev_device *, bool);
|
||||
void serdev_device_wait_until_sent(struct serdev_device *, long);
|
||||
int serdev_device_get_tiocm(struct serdev_device *);
|
||||
int serdev_device_set_tiocm(struct serdev_device *, int, int);
|
||||
int serdev_device_write_buf(struct serdev_device *, const unsigned char *, size_t);
|
||||
void serdev_device_write_flush(struct serdev_device *);
|
||||
int serdev_device_write_room(struct serdev_device *);
|
||||
@ -223,6 +231,15 @@ static inline unsigned int serdev_device_set_baudrate(struct serdev_device *sdev
|
||||
return 0;
|
||||
}
|
||||
static inline void serdev_device_set_flow_control(struct serdev_device *sdev, bool enable) {}
|
||||
static inline void serdev_device_wait_until_sent(struct serdev_device *sdev, long timeout) {}
|
||||
static inline int serdev_device_get_tiocm(struct serdev_device *serdev)
|
||||
{
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
static inline int serdev_device_set_tiocm(struct serdev_device *serdev, int set, int clear)
|
||||
{
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
static inline int serdev_device_write_buf(struct serdev_device *sdev, const unsigned char *buf, size_t count)
|
||||
{
|
||||
return -ENODEV;
|
||||
@ -238,6 +255,36 @@ static inline int serdev_device_write_room(struct serdev_device *sdev)
|
||||
|
||||
#endif /* CONFIG_SERIAL_DEV_BUS */
|
||||
|
||||
static inline bool serdev_device_get_cts(struct serdev_device *serdev)
|
||||
{
|
||||
int status = serdev_device_get_tiocm(serdev);
|
||||
return !!(status & TIOCM_CTS);
|
||||
}
|
||||
|
||||
static inline int serdev_device_wait_for_cts(struct serdev_device *serdev, bool state, int timeout_ms)
|
||||
{
|
||||
unsigned long timeout;
|
||||
bool signal;
|
||||
|
||||
timeout = jiffies + msecs_to_jiffies(timeout_ms);
|
||||
while (time_is_after_jiffies(timeout)) {
|
||||
signal = serdev_device_get_cts(serdev);
|
||||
if (signal == state)
|
||||
return 0;
|
||||
usleep_range(1000, 2000);
|
||||
}
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static inline int serdev_device_set_rts(struct serdev_device *serdev, bool enable)
|
||||
{
|
||||
if (enable)
|
||||
return serdev_device_set_tiocm(serdev, TIOCM_RTS, 0);
|
||||
else
|
||||
return serdev_device_set_tiocm(serdev, 0, TIOCM_RTS);
|
||||
}
|
||||
|
||||
/*
|
||||
* serdev hooks into TTY core
|
||||
*/
|
||||
|
@ -198,6 +198,21 @@ static inline void lowpan_iphc_uncompress_eui64_lladdr(struct in6_addr *ipaddr,
|
||||
ipaddr->s6_addr[8] ^= 0x02;
|
||||
}
|
||||
|
||||
static inline void lowpan_iphc_uncompress_eui48_lladdr(struct in6_addr *ipaddr,
|
||||
const void *lladdr)
|
||||
{
|
||||
/* fe:80::XXXX:XXff:feXX:XXXX
|
||||
* \_________________/
|
||||
* hwaddr
|
||||
*/
|
||||
ipaddr->s6_addr[0] = 0xFE;
|
||||
ipaddr->s6_addr[1] = 0x80;
|
||||
memcpy(&ipaddr->s6_addr[8], lladdr, 3);
|
||||
ipaddr->s6_addr[11] = 0xFF;
|
||||
ipaddr->s6_addr[12] = 0xFE;
|
||||
memcpy(&ipaddr->s6_addr[13], lladdr + 3, 3);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
/* print data in line */
|
||||
static inline void raw_dump_inline(const char *caller, char *msg,
|
||||
|
@ -282,7 +282,7 @@ struct l2cap_conn_rsp {
|
||||
#define L2CAP_CR_BAD_KEY_SIZE 0x0007
|
||||
#define L2CAP_CR_ENCRYPTION 0x0008
|
||||
#define L2CAP_CR_INVALID_SCID 0x0009
|
||||
#define L2CAP_CR_SCID_IN_USE 0x0010
|
||||
#define L2CAP_CR_SCID_IN_USE 0x000A
|
||||
|
||||
/* connect/create channel status */
|
||||
#define L2CAP_CS_NO_INFO 0x0000
|
||||
|
@ -21,6 +21,8 @@
|
||||
SOFTWARE IS DISCLAIMED.
|
||||
*/
|
||||
|
||||
#include <linux/refcount.h>
|
||||
|
||||
#ifndef __RFCOMM_H
|
||||
#define __RFCOMM_H
|
||||
|
||||
@ -174,7 +176,7 @@ struct rfcomm_dlc {
|
||||
struct mutex lock;
|
||||
unsigned long state;
|
||||
unsigned long flags;
|
||||
atomic_t refcnt;
|
||||
refcount_t refcnt;
|
||||
u8 dlci;
|
||||
u8 addr;
|
||||
u8 priority;
|
||||
@ -247,12 +249,12 @@ struct rfcomm_dlc *rfcomm_dlc_exists(bdaddr_t *src, bdaddr_t *dst, u8 channel);
|
||||
|
||||
static inline void rfcomm_dlc_hold(struct rfcomm_dlc *d)
|
||||
{
|
||||
atomic_inc(&d->refcnt);
|
||||
refcount_inc(&d->refcnt);
|
||||
}
|
||||
|
||||
static inline void rfcomm_dlc_put(struct rfcomm_dlc *d)
|
||||
{
|
||||
if (atomic_dec_and_test(&d->refcnt))
|
||||
if (refcount_dec_and_test(&d->refcnt))
|
||||
rfcomm_dlc_free(d);
|
||||
}
|
||||
|
||||
|
@ -23,10 +23,18 @@ int lowpan_register_netdevice(struct net_device *dev,
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
dev->addr_len = EUI64_ADDR_LEN;
|
||||
switch (lltype) {
|
||||
case LOWPAN_LLTYPE_IEEE802154:
|
||||
dev->addr_len = EUI64_ADDR_LEN;
|
||||
break;
|
||||
|
||||
case LOWPAN_LLTYPE_BTLE:
|
||||
dev->addr_len = ETH_ALEN;
|
||||
break;
|
||||
}
|
||||
|
||||
dev->type = ARPHRD_6LOWPAN;
|
||||
dev->mtu = IPV6_MIN_MTU;
|
||||
dev->priv_flags |= IFF_NO_QUEUE;
|
||||
|
||||
lowpan_dev(dev)->lltype = lltype;
|
||||
|
||||
|
@ -278,6 +278,23 @@ lowpan_iphc_ctx_get_by_mcast_addr(const struct net_device *dev,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void lowpan_iphc_uncompress_lladdr(const struct net_device *dev,
|
||||
struct in6_addr *ipaddr,
|
||||
const void *lladdr)
|
||||
{
|
||||
switch (dev->addr_len) {
|
||||
case ETH_ALEN:
|
||||
lowpan_iphc_uncompress_eui48_lladdr(ipaddr, lladdr);
|
||||
break;
|
||||
case EUI64_ADDR_LEN:
|
||||
lowpan_iphc_uncompress_eui64_lladdr(ipaddr, lladdr);
|
||||
break;
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Uncompress address function for source and
|
||||
* destination address(non-multicast).
|
||||
*
|
||||
@ -320,7 +337,7 @@ static int lowpan_iphc_uncompress_addr(struct sk_buff *skb,
|
||||
lowpan_iphc_uncompress_802154_lladdr(ipaddr, lladdr);
|
||||
break;
|
||||
default:
|
||||
lowpan_iphc_uncompress_eui64_lladdr(ipaddr, lladdr);
|
||||
lowpan_iphc_uncompress_lladdr(dev, ipaddr, lladdr);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@ -381,7 +398,7 @@ static int lowpan_iphc_uncompress_ctx_addr(struct sk_buff *skb,
|
||||
lowpan_iphc_uncompress_802154_lladdr(ipaddr, lladdr);
|
||||
break;
|
||||
default:
|
||||
lowpan_iphc_uncompress_eui64_lladdr(ipaddr, lladdr);
|
||||
lowpan_iphc_uncompress_lladdr(dev, ipaddr, lladdr);
|
||||
break;
|
||||
}
|
||||
ipv6_addr_prefix_copy(ipaddr, &ctx->pfx, ctx->plen);
|
||||
@ -666,6 +683,8 @@ int lowpan_header_decompress(struct sk_buff *skb, const struct net_device *dev,
|
||||
|
||||
switch (iphc1 & (LOWPAN_IPHC_M | LOWPAN_IPHC_DAC)) {
|
||||
case LOWPAN_IPHC_M | LOWPAN_IPHC_DAC:
|
||||
skb->pkt_type = PACKET_BROADCAST;
|
||||
|
||||
spin_lock_bh(&lowpan_dev(dev)->ctx.lock);
|
||||
ci = lowpan_iphc_ctx_get_by_id(dev, LOWPAN_IPHC_CID_DCI(cid));
|
||||
if (!ci) {
|
||||
@ -681,11 +700,15 @@ int lowpan_header_decompress(struct sk_buff *skb, const struct net_device *dev,
|
||||
spin_unlock_bh(&lowpan_dev(dev)->ctx.lock);
|
||||
break;
|
||||
case LOWPAN_IPHC_M:
|
||||
skb->pkt_type = PACKET_BROADCAST;
|
||||
|
||||
/* multicast */
|
||||
err = lowpan_uncompress_multicast_daddr(skb, &hdr.daddr,
|
||||
iphc1 & LOWPAN_IPHC_DAM_MASK);
|
||||
break;
|
||||
case LOWPAN_IPHC_DAC:
|
||||
skb->pkt_type = PACKET_HOST;
|
||||
|
||||
spin_lock_bh(&lowpan_dev(dev)->ctx.lock);
|
||||
ci = lowpan_iphc_ctx_get_by_id(dev, LOWPAN_IPHC_CID_DCI(cid));
|
||||
if (!ci) {
|
||||
@ -701,6 +724,8 @@ int lowpan_header_decompress(struct sk_buff *skb, const struct net_device *dev,
|
||||
spin_unlock_bh(&lowpan_dev(dev)->ctx.lock);
|
||||
break;
|
||||
default:
|
||||
skb->pkt_type = PACKET_HOST;
|
||||
|
||||
err = lowpan_iphc_uncompress_addr(skb, dev, &hdr.daddr,
|
||||
iphc1 & LOWPAN_IPHC_DAM_MASK,
|
||||
daddr);
|
||||
@ -802,6 +827,21 @@ lowpan_iphc_compress_ctx_802154_lladdr(const struct in6_addr *ipaddr,
|
||||
return lladdr_compress;
|
||||
}
|
||||
|
||||
static bool lowpan_iphc_addr_equal(const struct net_device *dev,
|
||||
const struct lowpan_iphc_ctx *ctx,
|
||||
const struct in6_addr *ipaddr,
|
||||
const void *lladdr)
|
||||
{
|
||||
struct in6_addr tmp = {};
|
||||
|
||||
lowpan_iphc_uncompress_lladdr(dev, &tmp, lladdr);
|
||||
|
||||
if (ctx)
|
||||
ipv6_addr_prefix_copy(&tmp, &ctx->pfx, ctx->plen);
|
||||
|
||||
return ipv6_addr_equal(&tmp, ipaddr);
|
||||
}
|
||||
|
||||
static u8 lowpan_compress_ctx_addr(u8 **hc_ptr, const struct net_device *dev,
|
||||
const struct in6_addr *ipaddr,
|
||||
const struct lowpan_iphc_ctx *ctx,
|
||||
@ -819,13 +859,7 @@ static u8 lowpan_compress_ctx_addr(u8 **hc_ptr, const struct net_device *dev,
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* check for SAM/DAM = 11 */
|
||||
memcpy(&tmp.s6_addr[8], lladdr, EUI64_ADDR_LEN);
|
||||
/* second bit-flip (Universe/Local) is done according RFC2464 */
|
||||
tmp.s6_addr[8] ^= 0x02;
|
||||
/* context information are always used */
|
||||
ipv6_addr_prefix_copy(&tmp, &ctx->pfx, ctx->plen);
|
||||
if (ipv6_addr_equal(&tmp, ipaddr)) {
|
||||
if (lowpan_iphc_addr_equal(dev, ctx, ipaddr, lladdr)) {
|
||||
dam = LOWPAN_IPHC_DAM_11;
|
||||
goto out;
|
||||
}
|
||||
@ -921,11 +955,12 @@ static u8 lowpan_compress_addr_64(u8 **hc_ptr, const struct net_device *dev,
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (is_addr_mac_addr_based(ipaddr, lladdr)) {
|
||||
dam = LOWPAN_IPHC_DAM_11; /* 0-bits */
|
||||
if (lowpan_iphc_addr_equal(dev, NULL, ipaddr, lladdr)) {
|
||||
dam = LOWPAN_IPHC_DAM_11;
|
||||
pr_debug("address compression 0 bits\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <net/ipv6.h>
|
||||
#include <net/ip6_route.h>
|
||||
#include <net/addrconf.h>
|
||||
#include <net/pkt_sched.h>
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
@ -38,7 +39,6 @@ struct skb_cb {
|
||||
struct in6_addr addr;
|
||||
struct in6_addr gw;
|
||||
struct l2cap_chan *chan;
|
||||
int status;
|
||||
};
|
||||
#define lowpan_cb(skb) ((struct skb_cb *)((skb)->cb))
|
||||
|
||||
@ -64,7 +64,7 @@ struct lowpan_peer {
|
||||
struct l2cap_chan *chan;
|
||||
|
||||
/* peer addresses in various formats */
|
||||
unsigned char eui64_addr[EUI64_ADDR_LEN];
|
||||
unsigned char lladdr[ETH_ALEN];
|
||||
struct in6_addr peer_addr;
|
||||
};
|
||||
|
||||
@ -270,28 +270,20 @@ static int give_skb_to_upper(struct sk_buff *skb, struct net_device *dev)
|
||||
}
|
||||
|
||||
static int iphc_decompress(struct sk_buff *skb, struct net_device *netdev,
|
||||
struct l2cap_chan *chan)
|
||||
struct lowpan_peer *peer)
|
||||
{
|
||||
const u8 *saddr, *daddr;
|
||||
const u8 *saddr;
|
||||
struct lowpan_btle_dev *dev;
|
||||
struct lowpan_peer *peer;
|
||||
|
||||
dev = lowpan_btle_dev(netdev);
|
||||
|
||||
rcu_read_lock();
|
||||
peer = __peer_lookup_chan(dev, chan);
|
||||
rcu_read_unlock();
|
||||
if (!peer)
|
||||
return -EINVAL;
|
||||
saddr = peer->lladdr;
|
||||
|
||||
saddr = peer->eui64_addr;
|
||||
daddr = dev->netdev->dev_addr;
|
||||
|
||||
return lowpan_header_decompress(skb, netdev, daddr, saddr);
|
||||
return lowpan_header_decompress(skb, netdev, netdev->dev_addr, saddr);
|
||||
}
|
||||
|
||||
static int recv_pkt(struct sk_buff *skb, struct net_device *dev,
|
||||
struct l2cap_chan *chan)
|
||||
struct lowpan_peer *peer)
|
||||
{
|
||||
struct sk_buff *local_skb;
|
||||
int ret;
|
||||
@ -344,8 +336,9 @@ static int recv_pkt(struct sk_buff *skb, struct net_device *dev,
|
||||
|
||||
local_skb->dev = dev;
|
||||
|
||||
ret = iphc_decompress(local_skb, dev, chan);
|
||||
ret = iphc_decompress(local_skb, dev, peer);
|
||||
if (ret < 0) {
|
||||
BT_DBG("iphc_decompress failed: %d", ret);
|
||||
kfree_skb(local_skb);
|
||||
goto drop;
|
||||
}
|
||||
@ -365,6 +358,7 @@ static int recv_pkt(struct sk_buff *skb, struct net_device *dev,
|
||||
consume_skb(local_skb);
|
||||
consume_skb(skb);
|
||||
} else {
|
||||
BT_DBG("unknown packet type");
|
||||
goto drop;
|
||||
}
|
||||
|
||||
@ -390,7 +384,7 @@ static int chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
|
||||
if (!dev || !dev->netdev)
|
||||
return -ENOENT;
|
||||
|
||||
err = recv_pkt(skb, dev->netdev, chan);
|
||||
err = recv_pkt(skb, dev->netdev, peer);
|
||||
if (err) {
|
||||
BT_DBG("recv pkt %d", err);
|
||||
err = -EAGAIN;
|
||||
@ -399,37 +393,6 @@ static int chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
|
||||
return err;
|
||||
}
|
||||
|
||||
static u8 get_addr_type_from_eui64(u8 byte)
|
||||
{
|
||||
/* Is universal(0) or local(1) bit */
|
||||
return ((byte & 0x02) ? BDADDR_LE_RANDOM : BDADDR_LE_PUBLIC);
|
||||
}
|
||||
|
||||
static void copy_to_bdaddr(struct in6_addr *ip6_daddr, bdaddr_t *addr)
|
||||
{
|
||||
u8 *eui64 = ip6_daddr->s6_addr + 8;
|
||||
|
||||
addr->b[0] = eui64[7];
|
||||
addr->b[1] = eui64[6];
|
||||
addr->b[2] = eui64[5];
|
||||
addr->b[3] = eui64[2];
|
||||
addr->b[4] = eui64[1];
|
||||
addr->b[5] = eui64[0];
|
||||
}
|
||||
|
||||
static void convert_dest_bdaddr(struct in6_addr *ip6_daddr,
|
||||
bdaddr_t *addr, u8 *addr_type)
|
||||
{
|
||||
copy_to_bdaddr(ip6_daddr, addr);
|
||||
|
||||
/* We need to toggle the U/L bit that we got from IPv6 address
|
||||
* so that we get the proper address and type of the BD address.
|
||||
*/
|
||||
addr->b[5] ^= 0x02;
|
||||
|
||||
*addr_type = get_addr_type_from_eui64(addr->b[5]);
|
||||
}
|
||||
|
||||
static int setup_header(struct sk_buff *skb, struct net_device *netdev,
|
||||
bdaddr_t *peer_addr, u8 *peer_addr_type)
|
||||
{
|
||||
@ -437,8 +400,7 @@ static int setup_header(struct sk_buff *skb, struct net_device *netdev,
|
||||
struct ipv6hdr *hdr;
|
||||
struct lowpan_btle_dev *dev;
|
||||
struct lowpan_peer *peer;
|
||||
bdaddr_t addr, *any = BDADDR_ANY;
|
||||
u8 *daddr = any->b;
|
||||
u8 *daddr;
|
||||
int err, status = 0;
|
||||
|
||||
hdr = ipv6_hdr(skb);
|
||||
@ -449,34 +411,24 @@ static int setup_header(struct sk_buff *skb, struct net_device *netdev,
|
||||
|
||||
if (ipv6_addr_is_multicast(&ipv6_daddr)) {
|
||||
lowpan_cb(skb)->chan = NULL;
|
||||
daddr = NULL;
|
||||
} else {
|
||||
u8 addr_type;
|
||||
BT_DBG("dest IP %pI6c", &ipv6_daddr);
|
||||
|
||||
/* Get destination BT device from skb.
|
||||
* If there is no such peer then discard the packet.
|
||||
/* The packet might be sent to 6lowpan interface
|
||||
* because of routing (either via default route
|
||||
* or user set route) so get peer according to
|
||||
* the destination address.
|
||||
*/
|
||||
convert_dest_bdaddr(&ipv6_daddr, &addr, &addr_type);
|
||||
|
||||
BT_DBG("dest addr %pMR type %d IP %pI6c", &addr,
|
||||
addr_type, &ipv6_daddr);
|
||||
|
||||
peer = peer_lookup_ba(dev, &addr, addr_type);
|
||||
peer = peer_lookup_dst(dev, &ipv6_daddr, skb);
|
||||
if (!peer) {
|
||||
/* The packet might be sent to 6lowpan interface
|
||||
* because of routing (either via default route
|
||||
* or user set route) so get peer according to
|
||||
* the destination address.
|
||||
*/
|
||||
peer = peer_lookup_dst(dev, &ipv6_daddr, skb);
|
||||
if (!peer) {
|
||||
BT_DBG("no such peer %pMR found", &addr);
|
||||
return -ENOENT;
|
||||
}
|
||||
BT_DBG("no such peer");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
daddr = peer->eui64_addr;
|
||||
*peer_addr = addr;
|
||||
*peer_addr_type = addr_type;
|
||||
daddr = peer->lladdr;
|
||||
*peer_addr = peer->chan->dst;
|
||||
*peer_addr_type = peer->chan->dst_type;
|
||||
lowpan_cb(skb)->chan = peer->chan;
|
||||
|
||||
status = 1;
|
||||
@ -527,15 +479,8 @@ static int send_pkt(struct l2cap_chan *chan, struct sk_buff *skb,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!err)
|
||||
err = lowpan_cb(skb)->status;
|
||||
|
||||
if (err < 0) {
|
||||
if (err == -EAGAIN)
|
||||
netdev->stats.tx_dropped++;
|
||||
else
|
||||
netdev->stats.tx_errors++;
|
||||
}
|
||||
if (err < 0)
|
||||
netdev->stats.tx_errors++;
|
||||
|
||||
return err;
|
||||
}
|
||||
@ -647,9 +592,9 @@ static void netdev_setup(struct net_device *dev)
|
||||
{
|
||||
dev->hard_header_len = 0;
|
||||
dev->needed_tailroom = 0;
|
||||
dev->flags = IFF_RUNNING | IFF_POINTOPOINT |
|
||||
IFF_MULTICAST;
|
||||
dev->flags = IFF_RUNNING | IFF_MULTICAST;
|
||||
dev->watchdog_timeo = 0;
|
||||
dev->tx_queue_len = DEFAULT_TX_QUEUE_LEN;
|
||||
|
||||
dev->netdev_ops = &netdev_ops;
|
||||
dev->header_ops = &header_ops;
|
||||
@ -660,34 +605,6 @@ static struct device_type bt_type = {
|
||||
.name = "bluetooth",
|
||||
};
|
||||
|
||||
static void set_addr(u8 *eui, u8 *addr, u8 addr_type)
|
||||
{
|
||||
/* addr is the BT address in little-endian format */
|
||||
eui[0] = addr[5];
|
||||
eui[1] = addr[4];
|
||||
eui[2] = addr[3];
|
||||
eui[3] = 0xFF;
|
||||
eui[4] = 0xFE;
|
||||
eui[5] = addr[2];
|
||||
eui[6] = addr[1];
|
||||
eui[7] = addr[0];
|
||||
|
||||
/* Universal/local bit set, BT 6lowpan draft ch. 3.2.1 */
|
||||
if (addr_type == BDADDR_LE_PUBLIC)
|
||||
eui[0] &= ~0x02;
|
||||
else
|
||||
eui[0] |= 0x02;
|
||||
|
||||
BT_DBG("type %d addr %*phC", addr_type, 8, eui);
|
||||
}
|
||||
|
||||
static void set_dev_addr(struct net_device *netdev, bdaddr_t *addr,
|
||||
u8 addr_type)
|
||||
{
|
||||
netdev->addr_assign_type = NET_ADDR_PERM;
|
||||
set_addr(netdev->dev_addr, addr->b, addr_type);
|
||||
}
|
||||
|
||||
static void ifup(struct net_device *netdev)
|
||||
{
|
||||
int err;
|
||||
@ -746,16 +663,9 @@ static struct l2cap_chan *chan_create(void)
|
||||
return chan;
|
||||
}
|
||||
|
||||
static void set_ip_addr_bits(u8 addr_type, u8 *addr)
|
||||
{
|
||||
if (addr_type == BDADDR_LE_PUBLIC)
|
||||
*addr |= 0x02;
|
||||
else
|
||||
*addr &= ~0x02;
|
||||
}
|
||||
|
||||
static struct l2cap_chan *add_peer_chan(struct l2cap_chan *chan,
|
||||
struct lowpan_btle_dev *dev)
|
||||
struct lowpan_btle_dev *dev,
|
||||
bool new_netdev)
|
||||
{
|
||||
struct lowpan_peer *peer;
|
||||
|
||||
@ -766,19 +676,9 @@ static struct l2cap_chan *add_peer_chan(struct l2cap_chan *chan,
|
||||
peer->chan = chan;
|
||||
memset(&peer->peer_addr, 0, sizeof(struct in6_addr));
|
||||
|
||||
/* RFC 2464 ch. 5 */
|
||||
peer->peer_addr.s6_addr[0] = 0xFE;
|
||||
peer->peer_addr.s6_addr[1] = 0x80;
|
||||
set_addr((u8 *)&peer->peer_addr.s6_addr + 8, chan->dst.b,
|
||||
chan->dst_type);
|
||||
baswap((void *)peer->lladdr, &chan->dst);
|
||||
|
||||
memcpy(&peer->eui64_addr, (u8 *)&peer->peer_addr.s6_addr + 8,
|
||||
EUI64_ADDR_LEN);
|
||||
|
||||
/* IPv6 address needs to have the U/L bit set properly so toggle
|
||||
* it back here.
|
||||
*/
|
||||
set_ip_addr_bits(chan->dst_type, (u8 *)&peer->peer_addr.s6_addr + 8);
|
||||
lowpan_iphc_uncompress_eui48_lladdr(&peer->peer_addr, peer->lladdr);
|
||||
|
||||
spin_lock(&devices_lock);
|
||||
INIT_LIST_HEAD(&peer->list);
|
||||
@ -786,7 +686,8 @@ static struct l2cap_chan *add_peer_chan(struct l2cap_chan *chan,
|
||||
spin_unlock(&devices_lock);
|
||||
|
||||
/* Notifying peers about us needs to be done without locks held */
|
||||
INIT_DELAYED_WORK(&dev->notify_peers, do_notify_peers);
|
||||
if (new_netdev)
|
||||
INIT_DELAYED_WORK(&dev->notify_peers, do_notify_peers);
|
||||
schedule_delayed_work(&dev->notify_peers, msecs_to_jiffies(100));
|
||||
|
||||
return peer->chan;
|
||||
@ -803,7 +704,8 @@ static int setup_netdev(struct l2cap_chan *chan, struct lowpan_btle_dev **dev)
|
||||
if (!netdev)
|
||||
return -ENOMEM;
|
||||
|
||||
set_dev_addr(netdev, &chan->src, chan->src_type);
|
||||
netdev->addr_assign_type = NET_ADDR_PERM;
|
||||
baswap((void *)netdev->dev_addr, &chan->src);
|
||||
|
||||
netdev->netdev_ops = &netdev_ops;
|
||||
SET_NETDEV_DEV(netdev, &chan->conn->hcon->hdev->dev);
|
||||
@ -843,6 +745,7 @@ static int setup_netdev(struct l2cap_chan *chan, struct lowpan_btle_dev **dev)
|
||||
static inline void chan_ready_cb(struct l2cap_chan *chan)
|
||||
{
|
||||
struct lowpan_btle_dev *dev;
|
||||
bool new_netdev = false;
|
||||
|
||||
dev = lookup_dev(chan->conn);
|
||||
|
||||
@ -853,12 +756,13 @@ static inline void chan_ready_cb(struct l2cap_chan *chan)
|
||||
l2cap_chan_del(chan, -ENOENT);
|
||||
return;
|
||||
}
|
||||
new_netdev = true;
|
||||
}
|
||||
|
||||
if (!try_module_get(THIS_MODULE))
|
||||
return;
|
||||
|
||||
add_peer_chan(chan, dev);
|
||||
add_peer_chan(chan, dev, new_netdev);
|
||||
ifup(dev->netdev);
|
||||
}
|
||||
|
||||
@ -964,26 +868,28 @@ static struct sk_buff *chan_alloc_skb_cb(struct l2cap_chan *chan,
|
||||
|
||||
static void chan_suspend_cb(struct l2cap_chan *chan)
|
||||
{
|
||||
struct sk_buff *skb = chan->data;
|
||||
struct lowpan_btle_dev *dev;
|
||||
|
||||
BT_DBG("chan %p conn %p skb %p", chan, chan->conn, skb);
|
||||
BT_DBG("chan %p suspend", chan);
|
||||
|
||||
if (!skb)
|
||||
dev = lookup_dev(chan->conn);
|
||||
if (!dev || !dev->netdev)
|
||||
return;
|
||||
|
||||
lowpan_cb(skb)->status = -EAGAIN;
|
||||
netif_stop_queue(dev->netdev);
|
||||
}
|
||||
|
||||
static void chan_resume_cb(struct l2cap_chan *chan)
|
||||
{
|
||||
struct sk_buff *skb = chan->data;
|
||||
struct lowpan_btle_dev *dev;
|
||||
|
||||
BT_DBG("chan %p conn %p skb %p", chan, chan->conn, skb);
|
||||
BT_DBG("chan %p resume", chan);
|
||||
|
||||
if (!skb)
|
||||
dev = lookup_dev(chan->conn);
|
||||
if (!dev || !dev->netdev)
|
||||
return;
|
||||
|
||||
lowpan_cb(skb)->status = 0;
|
||||
netif_wake_queue(dev->netdev);
|
||||
}
|
||||
|
||||
static long chan_get_sndtimeo_cb(struct l2cap_chan *chan)
|
||||
|
@ -159,12 +159,17 @@ void bt_accept_enqueue(struct sock *parent, struct sock *sk)
|
||||
BT_DBG("parent %p, sk %p", parent, sk);
|
||||
|
||||
sock_hold(sk);
|
||||
lock_sock(sk);
|
||||
list_add_tail(&bt_sk(sk)->accept_q, &bt_sk(parent)->accept_q);
|
||||
bt_sk(sk)->parent = parent;
|
||||
release_sock(sk);
|
||||
parent->sk_ack_backlog++;
|
||||
}
|
||||
EXPORT_SYMBOL(bt_accept_enqueue);
|
||||
|
||||
/* Calling function must hold the sk lock.
|
||||
* bt_sk(sk)->parent must be non-NULL meaning sk is in the parent list.
|
||||
*/
|
||||
void bt_accept_unlink(struct sock *sk)
|
||||
{
|
||||
BT_DBG("sk %p state %d", sk, sk->sk_state);
|
||||
@ -183,11 +188,32 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock)
|
||||
|
||||
BT_DBG("parent %p", parent);
|
||||
|
||||
restart:
|
||||
list_for_each_entry_safe(s, n, &bt_sk(parent)->accept_q, accept_q) {
|
||||
sk = (struct sock *)s;
|
||||
|
||||
/* Prevent early freeing of sk due to unlink and sock_kill */
|
||||
sock_hold(sk);
|
||||
lock_sock(sk);
|
||||
|
||||
/* Check sk has not already been unlinked via
|
||||
* bt_accept_unlink() due to serialisation caused by sk locking
|
||||
*/
|
||||
if (!bt_sk(sk)->parent) {
|
||||
BT_DBG("sk %p, already unlinked", sk);
|
||||
release_sock(sk);
|
||||
sock_put(sk);
|
||||
|
||||
/* Restart the loop as sk is no longer in the list
|
||||
* and also avoid a potential infinite loop because
|
||||
* list_for_each_entry_safe() is not thread safe.
|
||||
*/
|
||||
goto restart;
|
||||
}
|
||||
|
||||
/* sk is safely in the parent list so reduce reference count */
|
||||
sock_put(sk);
|
||||
|
||||
/* FIXME: Is this check still needed */
|
||||
if (sk->sk_state == BT_CLOSED) {
|
||||
bt_accept_unlink(sk);
|
||||
|
@ -263,7 +263,7 @@ void amp_read_loc_assoc_frag(struct hci_dev *hdev, u8 phy_handle)
|
||||
struct hci_cp_read_local_amp_assoc cp;
|
||||
struct amp_assoc *loc_assoc = &hdev->loc_assoc;
|
||||
struct hci_request req;
|
||||
int err = 0;
|
||||
int err;
|
||||
|
||||
BT_DBG("%s handle %d", hdev->name, phy_handle);
|
||||
|
||||
@ -282,7 +282,7 @@ void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr)
|
||||
{
|
||||
struct hci_cp_read_local_amp_assoc cp;
|
||||
struct hci_request req;
|
||||
int err = 0;
|
||||
int err;
|
||||
|
||||
memset(&hdev->loc_assoc, 0, sizeof(struct amp_assoc));
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
@ -292,7 +292,7 @@ void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr)
|
||||
set_bit(READ_LOC_AMP_ASSOC, &mgr->state);
|
||||
hci_req_init(&req, hdev);
|
||||
hci_req_add(&req, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp);
|
||||
hci_req_run_skb(&req, read_local_amp_assoc_complete);
|
||||
err = hci_req_run_skb(&req, read_local_amp_assoc_complete);
|
||||
if (err < 0)
|
||||
a2mp_send_getampassoc_rsp(hdev, A2MP_STATUS_INVALID_CTRL_ID);
|
||||
}
|
||||
@ -303,7 +303,7 @@ void amp_read_loc_assoc_final_data(struct hci_dev *hdev,
|
||||
struct hci_cp_read_local_amp_assoc cp;
|
||||
struct amp_mgr *mgr = hcon->amp_mgr;
|
||||
struct hci_request req;
|
||||
int err = 0;
|
||||
int err;
|
||||
|
||||
cp.phy_handle = hcon->handle;
|
||||
cp.len_so_far = cpu_to_le16(0);
|
||||
@ -314,7 +314,7 @@ void amp_read_loc_assoc_final_data(struct hci_dev *hdev,
|
||||
/* Read Local AMP Assoc final link information data */
|
||||
hci_req_init(&req, hdev);
|
||||
hci_req_add(&req, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp);
|
||||
hci_req_run_skb(&req, read_local_amp_assoc_complete);
|
||||
err = hci_req_run_skb(&req, read_local_amp_assoc_complete);
|
||||
if (err < 0)
|
||||
a2mp_send_getampassoc_rsp(hdev, A2MP_STATUS_INVALID_CTRL_ID);
|
||||
}
|
||||
|
@ -2950,8 +2950,8 @@ struct hci_dev *hci_alloc_dev(void)
|
||||
hdev->le_adv_max_interval = 0x0800;
|
||||
hdev->le_scan_interval = 0x0060;
|
||||
hdev->le_scan_window = 0x0030;
|
||||
hdev->le_conn_min_interval = 0x0028;
|
||||
hdev->le_conn_max_interval = 0x0038;
|
||||
hdev->le_conn_min_interval = 0x0018;
|
||||
hdev->le_conn_max_interval = 0x0028;
|
||||
hdev->le_conn_latency = 0x0000;
|
||||
hdev->le_supv_timeout = 0x002a;
|
||||
hdev->le_def_tx_len = 0x001b;
|
||||
|
@ -2425,6 +2425,22 @@ static int l2cap_segment_le_sdu(struct l2cap_chan *chan,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void l2cap_le_flowctl_send(struct l2cap_chan *chan)
|
||||
{
|
||||
int sent = 0;
|
||||
|
||||
BT_DBG("chan %p", chan);
|
||||
|
||||
while (chan->tx_credits && !skb_queue_empty(&chan->tx_q)) {
|
||||
l2cap_do_send(chan, skb_dequeue(&chan->tx_q));
|
||||
chan->tx_credits--;
|
||||
sent++;
|
||||
}
|
||||
|
||||
BT_DBG("Sent %d credits %u queued %u", sent, chan->tx_credits,
|
||||
skb_queue_len(&chan->tx_q));
|
||||
}
|
||||
|
||||
int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
@ -2458,9 +2474,6 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len)
|
||||
if (len > chan->omtu)
|
||||
return -EMSGSIZE;
|
||||
|
||||
if (!chan->tx_credits)
|
||||
return -EAGAIN;
|
||||
|
||||
__skb_queue_head_init(&seg_queue);
|
||||
|
||||
err = l2cap_segment_le_sdu(chan, &seg_queue, msg, len);
|
||||
@ -2475,10 +2488,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len)
|
||||
|
||||
skb_queue_splice_tail_init(&seg_queue, &chan->tx_q);
|
||||
|
||||
while (chan->tx_credits && !skb_queue_empty(&chan->tx_q)) {
|
||||
l2cap_do_send(chan, skb_dequeue(&chan->tx_q));
|
||||
chan->tx_credits--;
|
||||
}
|
||||
l2cap_le_flowctl_send(chan);
|
||||
|
||||
if (!chan->tx_credits)
|
||||
chan->ops->suspend(chan);
|
||||
@ -5570,10 +5580,8 @@ static inline int l2cap_le_credits(struct l2cap_conn *conn,
|
||||
|
||||
chan->tx_credits += credits;
|
||||
|
||||
while (chan->tx_credits && !skb_queue_empty(&chan->tx_q)) {
|
||||
l2cap_do_send(chan, skb_dequeue(&chan->tx_q));
|
||||
chan->tx_credits--;
|
||||
}
|
||||
/* Resume sending */
|
||||
l2cap_le_flowctl_send(chan);
|
||||
|
||||
if (chan->tx_credits)
|
||||
chan->ops->resume(chan);
|
||||
|
@ -311,7 +311,7 @@ struct rfcomm_dlc *rfcomm_dlc_alloc(gfp_t prio)
|
||||
|
||||
skb_queue_head_init(&d->tx_queue);
|
||||
mutex_init(&d->lock);
|
||||
atomic_set(&d->refcnt, 1);
|
||||
refcount_set(&d->refcnt, 1);
|
||||
|
||||
rfcomm_dlc_clear_state(d);
|
||||
|
||||
@ -342,7 +342,7 @@ static void rfcomm_dlc_unlink(struct rfcomm_dlc *d)
|
||||
{
|
||||
struct rfcomm_session *s = d->session;
|
||||
|
||||
BT_DBG("dlc %p refcnt %d session %p", d, atomic_read(&d->refcnt), s);
|
||||
BT_DBG("dlc %p refcnt %d session %p", d, refcount_read(&d->refcnt), s);
|
||||
|
||||
list_del(&d->list);
|
||||
d->session = NULL;
|
||||
|
@ -2073,12 +2073,23 @@ static void addrconf_leave_anycast(struct inet6_ifaddr *ifp)
|
||||
__ipv6_dev_ac_dec(ifp->idev, &addr);
|
||||
}
|
||||
|
||||
static int addrconf_ifid_eui64(u8 *eui, struct net_device *dev)
|
||||
static int addrconf_ifid_6lowpan(u8 *eui, struct net_device *dev)
|
||||
{
|
||||
if (dev->addr_len != EUI64_ADDR_LEN)
|
||||
switch (dev->addr_len) {
|
||||
case ETH_ALEN:
|
||||
memcpy(eui, dev->dev_addr, 3);
|
||||
eui[3] = 0xFF;
|
||||
eui[4] = 0xFE;
|
||||
memcpy(eui + 5, dev->dev_addr + 3, 3);
|
||||
break;
|
||||
case EUI64_ADDR_LEN:
|
||||
memcpy(eui, dev->dev_addr, EUI64_ADDR_LEN);
|
||||
eui[0] ^= 2;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
memcpy(eui, dev->dev_addr, EUI64_ADDR_LEN);
|
||||
eui[0] ^= 2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2170,7 +2181,7 @@ static int ipv6_generate_eui64(u8 *eui, struct net_device *dev)
|
||||
case ARPHRD_TUNNEL:
|
||||
return addrconf_ifid_gre(eui, dev);
|
||||
case ARPHRD_6LOWPAN:
|
||||
return addrconf_ifid_eui64(eui, dev);
|
||||
return addrconf_ifid_6lowpan(eui, dev);
|
||||
case ARPHRD_IEEE1394:
|
||||
return addrconf_ifid_ieee1394(eui, dev);
|
||||
case ARPHRD_TUNNEL6:
|
||||
|
Loading…
Reference in New Issue
Block a user