Merge branch 'for-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next

Johan Hedberg says:

====================
pull request: bluetooth-next 2015-03-02

Here's the first bluetooth-next pull request targeting the 4.1 kernel:

 - ieee802154/6lowpan cleanups
 - SCO routing to host interface support for the btmrvl driver
 - AMP code cleanups
 - Fixes to AMP HCI init sequence
 - Refactoring of the HCI callback mechanism
 - Added shutdown routine for Intel controllers in the btusb driver
 - New config option to enable/disable Bluetooth debugfs information
 - Fix for early data reception on L2CAP fixed channels

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:
David S. Miller 2015-03-02 14:47:12 -05:00
commit 70c836a4d1
38 changed files with 1286 additions and 492 deletions

View file

@ -6,11 +6,14 @@ Required properties:
- spi-max-frequency: maximal bus speed, should be set to 7500000 depends - spi-max-frequency: maximal bus speed, should be set to 7500000 depends
sync or async operation mode sync or async operation mode
- reg: the chipselect index - reg: the chipselect index
- interrupts: the interrupt generated by the device - interrupts: the interrupt generated by the device. Non high-level
can occur deadlocks while handling isr.
Optional properties: Optional properties:
- reset-gpio: GPIO spec for the rstn pin - reset-gpio: GPIO spec for the rstn pin
- sleep-gpio: GPIO spec for the slp_tr pin - sleep-gpio: GPIO spec for the slp_tr pin
- xtal-trim: u8 value for fine tuning the internal capacitance
arrays of xtal pins: 0 = +0 pF, 0xf = +4.5 pF
Example: Example:
@ -18,6 +21,7 @@ Example:
compatible = "atmel,at86rf231"; compatible = "atmel,at86rf231";
spi-max-frequency = <7500000>; spi-max-frequency = <7500000>;
reg = <0>; reg = <0>;
interrupts = <19 1>; interrupts = <19 4>;
interrupt-parent = <&gpio3>; interrupt-parent = <&gpio3>;
xtal-trim = /bits/ 8 <0x06>;
}; };

View file

@ -65,6 +65,7 @@ static const struct usb_device_id ath3k_table[] = {
/* Atheros AR3011 with sflash firmware*/ /* Atheros AR3011 with sflash firmware*/
{ USB_DEVICE(0x0489, 0xE027) }, { USB_DEVICE(0x0489, 0xE027) },
{ USB_DEVICE(0x0489, 0xE03D) }, { USB_DEVICE(0x0489, 0xE03D) },
{ USB_DEVICE(0x04F2, 0xAFF1) },
{ USB_DEVICE(0x0930, 0x0215) }, { USB_DEVICE(0x0930, 0x0215) },
{ USB_DEVICE(0x0CF3, 0x3002) }, { USB_DEVICE(0x0CF3, 0x3002) },
{ USB_DEVICE(0x0CF3, 0xE019) }, { USB_DEVICE(0x0CF3, 0xE019) },

View file

@ -111,6 +111,7 @@ struct btmrvl_private {
/* Vendor specific Bluetooth commands */ /* Vendor specific Bluetooth commands */
#define BT_CMD_PSCAN_WIN_REPORT_ENABLE 0xFC03 #define BT_CMD_PSCAN_WIN_REPORT_ENABLE 0xFC03
#define BT_CMD_ROUTE_SCO_TO_HOST 0xFC1D
#define BT_CMD_SET_BDADDR 0xFC22 #define BT_CMD_SET_BDADDR 0xFC22
#define BT_CMD_AUTO_SLEEP_MODE 0xFC23 #define BT_CMD_AUTO_SLEEP_MODE 0xFC23
#define BT_CMD_HOST_SLEEP_CONFIG 0xFC59 #define BT_CMD_HOST_SLEEP_CONFIG 0xFC59

View file

@ -230,6 +230,18 @@ int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, u8 subcmd)
} }
EXPORT_SYMBOL_GPL(btmrvl_send_module_cfg_cmd); EXPORT_SYMBOL_GPL(btmrvl_send_module_cfg_cmd);
static int btmrvl_enable_sco_routing_to_host(struct btmrvl_private *priv)
{
int ret;
u8 subcmd = 0;
ret = btmrvl_send_sync_cmd(priv, BT_CMD_ROUTE_SCO_TO_HOST, &subcmd, 1);
if (ret)
BT_ERR("BT_CMD_ROUTE_SCO_TO_HOST command failed: %#x", ret);
return ret;
}
int btmrvl_pscan_window_reporting(struct btmrvl_private *priv, u8 subcmd) int btmrvl_pscan_window_reporting(struct btmrvl_private *priv, u8 subcmd)
{ {
struct btmrvl_sdio_card *card = priv->btmrvl_dev.card; struct btmrvl_sdio_card *card = priv->btmrvl_dev.card;
@ -558,6 +570,8 @@ static int btmrvl_setup(struct hci_dev *hdev)
btmrvl_check_device_tree(priv); btmrvl_check_device_tree(priv);
btmrvl_enable_sco_routing_to_host(priv);
btmrvl_pscan_window_reporting(priv, 0x01); btmrvl_pscan_window_reporting(priv, 0x01);
priv->btmrvl_dev.psmode = 1; priv->btmrvl_dev.psmode = 1;

View file

@ -159,6 +159,7 @@ static const struct usb_device_id blacklist_table[] = {
/* Atheros 3011 with sflash firmware */ /* Atheros 3011 with sflash firmware */
{ USB_DEVICE(0x0489, 0xe027), .driver_info = BTUSB_IGNORE }, { USB_DEVICE(0x0489, 0xe027), .driver_info = BTUSB_IGNORE },
{ USB_DEVICE(0x0489, 0xe03d), .driver_info = BTUSB_IGNORE }, { USB_DEVICE(0x0489, 0xe03d), .driver_info = BTUSB_IGNORE },
{ USB_DEVICE(0x04f2, 0xaff1), .driver_info = BTUSB_IGNORE },
{ USB_DEVICE(0x0930, 0x0215), .driver_info = BTUSB_IGNORE }, { USB_DEVICE(0x0930, 0x0215), .driver_info = BTUSB_IGNORE },
{ USB_DEVICE(0x0cf3, 0x3002), .driver_info = BTUSB_IGNORE }, { USB_DEVICE(0x0cf3, 0x3002), .driver_info = BTUSB_IGNORE },
{ USB_DEVICE(0x0cf3, 0xe019), .driver_info = BTUSB_IGNORE }, { USB_DEVICE(0x0cf3, 0xe019), .driver_info = BTUSB_IGNORE },
@ -338,16 +339,6 @@ struct btusb_data {
int (*recv_bulk)(struct btusb_data *data, void *buffer, int count); int (*recv_bulk)(struct btusb_data *data, void *buffer, int count);
}; };
static int btusb_wait_on_bit_timeout(void *word, int bit, unsigned long timeout,
unsigned mode)
{
might_sleep();
if (!test_bit(bit, word))
return 0;
return out_of_line_wait_on_bit_timeout(word, bit, bit_wait_timeout,
mode, timeout);
}
static inline void btusb_free_frags(struct btusb_data *data) static inline void btusb_free_frags(struct btusb_data *data)
{ {
unsigned long flags; unsigned long flags;
@ -2196,9 +2187,9 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
* and thus just timeout if that happens and fail the setup * and thus just timeout if that happens and fail the setup
* of this device. * of this device.
*/ */
err = btusb_wait_on_bit_timeout(&data->flags, BTUSB_DOWNLOADING, err = wait_on_bit_timeout(&data->flags, BTUSB_DOWNLOADING,
msecs_to_jiffies(5000), TASK_INTERRUPTIBLE,
TASK_INTERRUPTIBLE); msecs_to_jiffies(5000));
if (err == 1) { if (err == 1) {
BT_ERR("%s: Firmware loading interrupted", hdev->name); BT_ERR("%s: Firmware loading interrupted", hdev->name);
err = -EINTR; err = -EINTR;
@ -2249,9 +2240,9 @@ done:
*/ */
BT_INFO("%s: Waiting for device to boot", hdev->name); BT_INFO("%s: Waiting for device to boot", hdev->name);
err = btusb_wait_on_bit_timeout(&data->flags, BTUSB_BOOTING, err = wait_on_bit_timeout(&data->flags, BTUSB_BOOTING,
msecs_to_jiffies(1000), TASK_INTERRUPTIBLE,
TASK_INTERRUPTIBLE); msecs_to_jiffies(1000));
if (err == 1) { if (err == 1) {
BT_ERR("%s: Device boot interrupted", hdev->name); BT_ERR("%s: Device boot interrupted", hdev->name);
@ -2331,6 +2322,27 @@ static int btusb_set_bdaddr_intel(struct hci_dev *hdev, const bdaddr_t *bdaddr)
return 0; return 0;
} }
static int btusb_shutdown_intel(struct hci_dev *hdev)
{
struct sk_buff *skb;
long ret;
/* Some platforms have an issue with BT LED when the interface is
* down or BT radio is turned off, which takes 5 seconds to BT LED
* goes off. This command turns off the BT LED immediately.
*/
skb = __hci_cmd_sync(hdev, 0xfc3f, 0, NULL, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
ret = PTR_ERR(skb);
BT_ERR("%s: turning off Intel device LED failed (%ld)",
hdev->name, ret);
return ret;
}
kfree_skb(skb);
return 0;
}
static int btusb_set_bdaddr_marvell(struct hci_dev *hdev, static int btusb_set_bdaddr_marvell(struct hci_dev *hdev,
const bdaddr_t *bdaddr) const bdaddr_t *bdaddr)
{ {
@ -2354,6 +2366,23 @@ static int btusb_set_bdaddr_marvell(struct hci_dev *hdev,
return 0; return 0;
} }
static const struct {
u16 subver;
const char *name;
} bcm_subver_table[] = {
{ 0x210b, "BCM43142A0" }, /* 001.001.011 */
{ 0x2112, "BCM4314A0" }, /* 001.001.018 */
{ 0x2118, "BCM20702A0" }, /* 001.001.024 */
{ 0x2126, "BCM4335A0" }, /* 001.001.038 */
{ 0x220e, "BCM20702A1" }, /* 001.002.014 */
{ 0x230f, "BCM4354A2" }, /* 001.003.015 */
{ 0x4106, "BCM4335B0" }, /* 002.001.006 */
{ 0x410e, "BCM20702B0" }, /* 002.001.014 */
{ 0x6109, "BCM4335C0" }, /* 003.001.009 */
{ 0x610c, "BCM4354" }, /* 003.001.012 */
{ }
};
#define BDADDR_BCM20702A0 (&(bdaddr_t) {{0x00, 0xa0, 0x02, 0x70, 0x20, 0x00}}) #define BDADDR_BCM20702A0 (&(bdaddr_t) {{0x00, 0xa0, 0x02, 0x70, 0x20, 0x00}})
static int btusb_setup_bcm_patchram(struct hci_dev *hdev) static int btusb_setup_bcm_patchram(struct hci_dev *hdev)
@ -2366,29 +2395,20 @@ static int btusb_setup_bcm_patchram(struct hci_dev *hdev)
size_t fw_size; size_t fw_size;
const struct hci_command_hdr *cmd; const struct hci_command_hdr *cmd;
const u8 *cmd_param; const u8 *cmd_param;
u16 opcode; u16 opcode, subver, rev;
const char *hw_name = NULL;
struct sk_buff *skb; struct sk_buff *skb;
struct hci_rp_read_local_version *ver; struct hci_rp_read_local_version *ver;
struct hci_rp_read_bd_addr *bda; struct hci_rp_read_bd_addr *bda;
long ret; long ret;
int i;
snprintf(fw_name, sizeof(fw_name), "brcm/%s-%04x-%04x.hcd",
udev->product ? udev->product : "BCM",
le16_to_cpu(udev->descriptor.idVendor),
le16_to_cpu(udev->descriptor.idProduct));
ret = request_firmware(&fw, fw_name, &hdev->dev);
if (ret < 0) {
BT_INFO("%s: BCM: patch %s not found", hdev->name, fw_name);
return 0;
}
/* Reset */ /* Reset */
skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT); skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) { if (IS_ERR(skb)) {
ret = PTR_ERR(skb); ret = PTR_ERR(skb);
BT_ERR("%s: HCI_OP_RESET failed (%ld)", hdev->name, ret); BT_ERR("%s: HCI_OP_RESET failed (%ld)", hdev->name, ret);
goto done; return ret;
} }
kfree_skb(skb); kfree_skb(skb);
@ -2399,23 +2419,43 @@ static int btusb_setup_bcm_patchram(struct hci_dev *hdev)
ret = PTR_ERR(skb); ret = PTR_ERR(skb);
BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION failed (%ld)", BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION failed (%ld)",
hdev->name, ret); hdev->name, ret);
goto done; return ret;
} }
if (skb->len != sizeof(*ver)) { if (skb->len != sizeof(*ver)) {
BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION event length mismatch", BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION event length mismatch",
hdev->name); hdev->name);
kfree_skb(skb); kfree_skb(skb);
ret = -EIO; return -EIO;
goto done;
} }
ver = (struct hci_rp_read_local_version *)skb->data; ver = (struct hci_rp_read_local_version *)skb->data;
BT_INFO("%s: BCM: patching hci_ver=%02x hci_rev=%04x lmp_ver=%02x " rev = le16_to_cpu(ver->hci_rev);
"lmp_subver=%04x", hdev->name, ver->hci_ver, ver->hci_rev, subver = le16_to_cpu(ver->lmp_subver);
ver->lmp_ver, ver->lmp_subver);
kfree_skb(skb); kfree_skb(skb);
for (i = 0; bcm_subver_table[i].name; i++) {
if (subver == bcm_subver_table[i].subver) {
hw_name = bcm_subver_table[i].name;
break;
}
}
BT_INFO("%s: %s (%3.3u.%3.3u.%3.3u) build %4.4u", hdev->name,
hw_name ? : "BCM", (subver & 0x7000) >> 13,
(subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff);
snprintf(fw_name, sizeof(fw_name), "brcm/%s-%4.4x-%4.4x.hcd",
hw_name ? : "BCM",
le16_to_cpu(udev->descriptor.idVendor),
le16_to_cpu(udev->descriptor.idProduct));
ret = request_firmware(&fw, fw_name, &hdev->dev);
if (ret < 0) {
BT_INFO("%s: BCM: patch %s not found", hdev->name, fw_name);
return 0;
}
/* Start Download */ /* Start Download */
skb = __hci_cmd_sync(hdev, 0xfc2e, 0, NULL, HCI_INIT_TIMEOUT); skb = __hci_cmd_sync(hdev, 0xfc2e, 0, NULL, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) { if (IS_ERR(skb)) {
@ -2493,11 +2533,14 @@ reset_fw:
} }
ver = (struct hci_rp_read_local_version *)skb->data; ver = (struct hci_rp_read_local_version *)skb->data;
BT_INFO("%s: BCM: firmware hci_ver=%02x hci_rev=%04x lmp_ver=%02x " rev = le16_to_cpu(ver->hci_rev);
"lmp_subver=%04x", hdev->name, ver->hci_ver, ver->hci_rev, subver = le16_to_cpu(ver->lmp_subver);
ver->lmp_ver, ver->lmp_subver);
kfree_skb(skb); kfree_skb(skb);
BT_INFO("%s: %s (%3.3u.%3.3u.%3.3u) build %4.4u", hdev->name,
hw_name ? : "BCM", (subver & 0x7000) >> 13,
(subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff);
/* Read BD Address */ /* Read BD Address */
skb = __hci_cmd_sync(hdev, HCI_OP_READ_BD_ADDR, 0, NULL, skb = __hci_cmd_sync(hdev, HCI_OP_READ_BD_ADDR, 0, NULL,
HCI_INIT_TIMEOUT); HCI_INIT_TIMEOUT);
@ -2708,6 +2751,7 @@ static int btusb_probe(struct usb_interface *intf,
if (id->driver_info & BTUSB_INTEL) { if (id->driver_info & BTUSB_INTEL) {
hdev->setup = btusb_setup_intel; hdev->setup = btusb_setup_intel;
hdev->shutdown = btusb_shutdown_intel;
hdev->set_bdaddr = btusb_set_bdaddr_intel; hdev->set_bdaddr = btusb_set_bdaddr_intel;
set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks); set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
} }

View file

@ -46,8 +46,6 @@ struct at86rf2xx_chip_data {
u16 t_off_to_tx_on; u16 t_off_to_tx_on;
u16 t_frame; u16 t_frame;
u16 t_p_ack; u16 t_p_ack;
/* completion timeout for tx in msecs */
u16 t_tx_timeout;
int rssi_base_val; int rssi_base_val;
int (*set_channel)(struct at86rf230_local *, u8, u8); int (*set_channel)(struct at86rf230_local *, u8, u8);
@ -689,7 +687,7 @@ at86rf230_sync_state_change_complete(void *context)
static int static int
at86rf230_sync_state_change(struct at86rf230_local *lp, unsigned int state) at86rf230_sync_state_change(struct at86rf230_local *lp, unsigned int state)
{ {
int rc; unsigned long rc;
at86rf230_async_state_change(lp, &lp->state, state, at86rf230_async_state_change(lp, &lp->state, state,
at86rf230_sync_state_change_complete, at86rf230_sync_state_change_complete,
@ -1281,7 +1279,6 @@ static struct at86rf2xx_chip_data at86rf233_data = {
.t_off_to_tx_on = 80, .t_off_to_tx_on = 80,
.t_frame = 4096, .t_frame = 4096,
.t_p_ack = 545, .t_p_ack = 545,
.t_tx_timeout = 2000,
.rssi_base_val = -91, .rssi_base_val = -91,
.set_channel = at86rf23x_set_channel, .set_channel = at86rf23x_set_channel,
.get_desense_steps = at86rf23x_get_desens_steps .get_desense_steps = at86rf23x_get_desens_steps
@ -1295,7 +1292,6 @@ static struct at86rf2xx_chip_data at86rf231_data = {
.t_off_to_tx_on = 110, .t_off_to_tx_on = 110,
.t_frame = 4096, .t_frame = 4096,
.t_p_ack = 545, .t_p_ack = 545,
.t_tx_timeout = 2000,
.rssi_base_val = -91, .rssi_base_val = -91,
.set_channel = at86rf23x_set_channel, .set_channel = at86rf23x_set_channel,
.get_desense_steps = at86rf23x_get_desens_steps .get_desense_steps = at86rf23x_get_desens_steps
@ -1309,13 +1305,12 @@ static struct at86rf2xx_chip_data at86rf212_data = {
.t_off_to_tx_on = 200, .t_off_to_tx_on = 200,
.t_frame = 4096, .t_frame = 4096,
.t_p_ack = 545, .t_p_ack = 545,
.t_tx_timeout = 2000,
.rssi_base_val = -100, .rssi_base_val = -100,
.set_channel = at86rf212_set_channel, .set_channel = at86rf212_set_channel,
.get_desense_steps = at86rf212_get_desens_steps .get_desense_steps = at86rf212_get_desens_steps
}; };
static int at86rf230_hw_init(struct at86rf230_local *lp) static int at86rf230_hw_init(struct at86rf230_local *lp, u8 xtal_trim)
{ {
int rc, irq_type, irq_pol = IRQ_ACTIVE_HIGH; int rc, irq_type, irq_pol = IRQ_ACTIVE_HIGH;
unsigned int dvdd; unsigned int dvdd;
@ -1326,7 +1321,12 @@ static int at86rf230_hw_init(struct at86rf230_local *lp)
return rc; return rc;
irq_type = irq_get_trigger_type(lp->spi->irq); irq_type = irq_get_trigger_type(lp->spi->irq);
if (irq_type == IRQ_TYPE_EDGE_FALLING) if (irq_type == IRQ_TYPE_EDGE_RISING ||
irq_type == IRQ_TYPE_EDGE_FALLING)
dev_warn(&lp->spi->dev,
"Using edge triggered irq's are not recommended!\n");
if (irq_type == IRQ_TYPE_EDGE_FALLING ||
irq_type == IRQ_TYPE_LEVEL_LOW)
irq_pol = IRQ_ACTIVE_LOW; irq_pol = IRQ_ACTIVE_LOW;
rc = at86rf230_write_subreg(lp, SR_IRQ_POLARITY, irq_pol); rc = at86rf230_write_subreg(lp, SR_IRQ_POLARITY, irq_pol);
@ -1341,6 +1341,11 @@ static int at86rf230_hw_init(struct at86rf230_local *lp)
if (rc) if (rc)
return rc; return rc;
/* reset values differs in at86rf231 and at86rf233 */
rc = at86rf230_write_subreg(lp, SR_IRQ_MASK_MODE, 0);
if (rc)
return rc;
get_random_bytes(csma_seed, ARRAY_SIZE(csma_seed)); get_random_bytes(csma_seed, ARRAY_SIZE(csma_seed));
rc = at86rf230_write_subreg(lp, SR_CSMA_SEED_0, csma_seed[0]); rc = at86rf230_write_subreg(lp, SR_CSMA_SEED_0, csma_seed[0]);
if (rc) if (rc)
@ -1362,6 +1367,45 @@ static int at86rf230_hw_init(struct at86rf230_local *lp)
usleep_range(lp->data->t_sleep_cycle, usleep_range(lp->data->t_sleep_cycle,
lp->data->t_sleep_cycle + 100); lp->data->t_sleep_cycle + 100);
/* xtal_trim value is calculated by:
* CL = 0.5 * (CX + CTRIM + CPAR)
*
* whereas:
* CL = capacitor of used crystal
* CX = connected capacitors at xtal pins
* CPAR = in all at86rf2xx datasheets this is a constant value 3 pF,
* but this is different on each board setup. You need to fine
* tuning this value via CTRIM.
* CTRIM = variable capacitor setting. Resolution is 0.3 pF range is
* 0 pF upto 4.5 pF.
*
* Examples:
* atben transceiver:
*
* CL = 8 pF
* CX = 12 pF
* CPAR = 3 pF (We assume the magic constant from datasheet)
* CTRIM = 0.9 pF
*
* (12+0.9+3)/2 = 7.95 which is nearly at 8 pF
*
* xtal_trim = 0x3
*
* openlabs transceiver:
*
* CL = 16 pF
* CX = 22 pF
* CPAR = 3 pF (We assume the magic constant from datasheet)
* CTRIM = 4.5 pF
*
* (22+4.5+3)/2 = 14.75 which is the nearest value to 16 pF
*
* xtal_trim = 0xf
*/
rc = at86rf230_write_subreg(lp, SR_XTAL_TRIM, xtal_trim);
if (rc)
return rc;
rc = at86rf230_read_subreg(lp, SR_DVDD_OK, &dvdd); rc = at86rf230_read_subreg(lp, SR_DVDD_OK, &dvdd);
if (rc) if (rc)
return rc; return rc;
@ -1377,24 +1421,30 @@ static int at86rf230_hw_init(struct at86rf230_local *lp)
return at86rf230_write_subreg(lp, SR_SLOTTED_OPERATION, 0); return at86rf230_write_subreg(lp, SR_SLOTTED_OPERATION, 0);
} }
static struct at86rf230_platform_data * static int
at86rf230_get_pdata(struct spi_device *spi) at86rf230_get_pdata(struct spi_device *spi, int *rstn, int *slp_tr,
u8 *xtal_trim)
{ {
struct at86rf230_platform_data *pdata; struct at86rf230_platform_data *pdata = spi->dev.platform_data;
int ret;
if (!IS_ENABLED(CONFIG_OF) || !spi->dev.of_node) if (!IS_ENABLED(CONFIG_OF) || !spi->dev.of_node) {
return spi->dev.platform_data; if (!pdata)
return -ENOENT;
pdata = devm_kzalloc(&spi->dev, sizeof(*pdata), GFP_KERNEL); *rstn = pdata->rstn;
if (!pdata) *slp_tr = pdata->slp_tr;
goto done; *xtal_trim = pdata->xtal_trim;
return 0;
}
pdata->rstn = of_get_named_gpio(spi->dev.of_node, "reset-gpio", 0); *rstn = of_get_named_gpio(spi->dev.of_node, "reset-gpio", 0);
pdata->slp_tr = of_get_named_gpio(spi->dev.of_node, "sleep-gpio", 0); *slp_tr = of_get_named_gpio(spi->dev.of_node, "sleep-gpio", 0);
ret = of_property_read_u8(spi->dev.of_node, "xtal-trim", xtal_trim);
if (ret < 0 && ret != -EINVAL)
return ret;
spi->dev.platform_data = pdata; return 0;
done:
return pdata;
} }
static int static int
@ -1501,43 +1551,43 @@ at86rf230_setup_spi_messages(struct at86rf230_local *lp)
static int at86rf230_probe(struct spi_device *spi) static int at86rf230_probe(struct spi_device *spi)
{ {
struct at86rf230_platform_data *pdata;
struct ieee802154_hw *hw; struct ieee802154_hw *hw;
struct at86rf230_local *lp; struct at86rf230_local *lp;
unsigned int status; unsigned int status;
int rc, irq_type; int rc, irq_type, rstn, slp_tr;
u8 xtal_trim;
if (!spi->irq) { if (!spi->irq) {
dev_err(&spi->dev, "no IRQ specified\n"); dev_err(&spi->dev, "no IRQ specified\n");
return -EINVAL; return -EINVAL;
} }
pdata = at86rf230_get_pdata(spi); rc = at86rf230_get_pdata(spi, &rstn, &slp_tr, &xtal_trim);
if (!pdata) { if (rc < 0) {
dev_err(&spi->dev, "no platform_data\n"); dev_err(&spi->dev, "failed to parse platform_data: %d\n", rc);
return -EINVAL; return rc;
} }
if (gpio_is_valid(pdata->rstn)) { if (gpio_is_valid(rstn)) {
rc = devm_gpio_request_one(&spi->dev, pdata->rstn, rc = devm_gpio_request_one(&spi->dev, rstn,
GPIOF_OUT_INIT_HIGH, "rstn"); GPIOF_OUT_INIT_HIGH, "rstn");
if (rc) if (rc)
return rc; return rc;
} }
if (gpio_is_valid(pdata->slp_tr)) { if (gpio_is_valid(slp_tr)) {
rc = devm_gpio_request_one(&spi->dev, pdata->slp_tr, rc = devm_gpio_request_one(&spi->dev, slp_tr,
GPIOF_OUT_INIT_LOW, "slp_tr"); GPIOF_OUT_INIT_LOW, "slp_tr");
if (rc) if (rc)
return rc; return rc;
} }
/* Reset */ /* Reset */
if (gpio_is_valid(pdata->rstn)) { if (gpio_is_valid(rstn)) {
udelay(1); udelay(1);
gpio_set_value(pdata->rstn, 0); gpio_set_value(rstn, 0);
udelay(1); udelay(1);
gpio_set_value(pdata->rstn, 1); gpio_set_value(rstn, 1);
usleep_range(120, 240); usleep_range(120, 240);
} }
@ -1571,7 +1621,7 @@ static int at86rf230_probe(struct spi_device *spi)
spi_set_drvdata(spi, lp); spi_set_drvdata(spi, lp);
rc = at86rf230_hw_init(lp); rc = at86rf230_hw_init(lp, xtal_trim);
if (rc) if (rc)
goto free_dev; goto free_dev;

View file

@ -28,7 +28,8 @@
#include <asm/byteorder.h> #include <asm/byteorder.h>
#define IEEE802154_MTU 127 #define IEEE802154_MTU 127
#define IEEE802154_MIN_PSDU_LEN 5 #define IEEE802154_ACK_PSDU_LEN 5
#define IEEE802154_MIN_PSDU_LEN 9
#define IEEE802154_PAN_ID_BROADCAST 0xffff #define IEEE802154_PAN_ID_BROADCAST 0xffff
#define IEEE802154_ADDR_SHORT_BROADCAST 0xffff #define IEEE802154_ADDR_SHORT_BROADCAST 0xffff
@ -204,11 +205,18 @@ enum {
/** /**
* ieee802154_is_valid_psdu_len - check if psdu len is valid * ieee802154_is_valid_psdu_len - check if psdu len is valid
* available lengths:
* 0-4 Reserved
* 5 MPDU (Acknowledgment)
* 6-8 Reserved
* 9-127 MPDU
*
* @len: psdu len with (MHR + payload + MFR) * @len: psdu len with (MHR + payload + MFR)
*/ */
static inline bool ieee802154_is_valid_psdu_len(const u8 len) static inline bool ieee802154_is_valid_psdu_len(const u8 len)
{ {
return (len >= IEEE802154_MIN_PSDU_LEN && len <= IEEE802154_MTU); return (len == IEEE802154_ACK_PSDU_LEN ||
(len >= IEEE802154_MIN_PSDU_LEN && len <= IEEE802154_MTU));
} }
/** /**

View file

@ -22,6 +22,7 @@ struct at86rf230_platform_data {
int rstn; int rstn;
int slp_tr; int slp_tr;
int dig2; int dig2;
u8 xtal_trim;
}; };
#endif #endif

View file

@ -108,7 +108,7 @@ struct bt_uuid {
struct smp_csrk { struct smp_csrk {
bdaddr_t bdaddr; bdaddr_t bdaddr;
u8 bdaddr_type; u8 bdaddr_type;
u8 master; u8 type;
u8 val[16]; u8 val[16];
}; };
@ -373,6 +373,7 @@ struct hci_dev {
int (*close)(struct hci_dev *hdev); int (*close)(struct hci_dev *hdev);
int (*flush)(struct hci_dev *hdev); int (*flush)(struct hci_dev *hdev);
int (*setup)(struct hci_dev *hdev); int (*setup)(struct hci_dev *hdev);
int (*shutdown)(struct hci_dev *hdev);
int (*send)(struct hci_dev *hdev, struct sk_buff *skb); int (*send)(struct hci_dev *hdev, struct sk_buff *skb);
void (*notify)(struct hci_dev *hdev, unsigned int evt); void (*notify)(struct hci_dev *hdev, unsigned int evt);
void (*hw_error)(struct hci_dev *hdev, u8 code); void (*hw_error)(struct hci_dev *hdev, u8 code);
@ -498,19 +499,14 @@ struct hci_conn_params {
extern struct list_head hci_dev_list; extern struct list_head hci_dev_list;
extern struct list_head hci_cb_list; extern struct list_head hci_cb_list;
extern rwlock_t hci_dev_list_lock; extern rwlock_t hci_dev_list_lock;
extern rwlock_t hci_cb_list_lock; extern struct mutex hci_cb_list_lock;
/* ----- HCI interface to upper protocols ----- */ /* ----- HCI interface to upper protocols ----- */
int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr); int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr);
void l2cap_connect_cfm(struct hci_conn *hcon, u8 status);
int l2cap_disconn_ind(struct hci_conn *hcon); int l2cap_disconn_ind(struct hci_conn *hcon);
void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason);
int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt);
int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags); int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags);
int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags); int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags);
void sco_connect_cfm(struct hci_conn *hcon, __u8 status);
void sco_disconn_cfm(struct hci_conn *hcon, __u8 reason);
int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb); int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb);
/* ----- Inquiry cache ----- */ /* ----- Inquiry cache ----- */
@ -1050,28 +1046,6 @@ static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr,
} }
} }
static inline void hci_proto_connect_cfm(struct hci_conn *conn, __u8 status)
{
switch (conn->type) {
case ACL_LINK:
case LE_LINK:
l2cap_connect_cfm(conn, status);
break;
case SCO_LINK:
case ESCO_LINK:
sco_connect_cfm(conn, status);
break;
default:
BT_ERR("unknown link type %d", conn->type);
break;
}
if (conn->connect_cfm_cb)
conn->connect_cfm_cb(conn, status);
}
static inline int hci_proto_disconn_ind(struct hci_conn *conn) static inline int hci_proto_disconn_ind(struct hci_conn *conn)
{ {
if (conn->type != ACL_LINK && conn->type != LE_LINK) if (conn->type != ACL_LINK && conn->type != LE_LINK)
@ -1080,91 +1054,69 @@ static inline int hci_proto_disconn_ind(struct hci_conn *conn)
return l2cap_disconn_ind(conn); return l2cap_disconn_ind(conn);
} }
static inline void hci_proto_disconn_cfm(struct hci_conn *conn, __u8 reason)
{
switch (conn->type) {
case ACL_LINK:
case LE_LINK:
l2cap_disconn_cfm(conn, reason);
break;
case SCO_LINK:
case ESCO_LINK:
sco_disconn_cfm(conn, reason);
break;
/* L2CAP would be handled for BREDR chan */
case AMP_LINK:
break;
default:
BT_ERR("unknown link type %d", conn->type);
break;
}
if (conn->disconn_cfm_cb)
conn->disconn_cfm_cb(conn, reason);
}
static inline void hci_proto_auth_cfm(struct hci_conn *conn, __u8 status)
{
__u8 encrypt;
if (conn->type != ACL_LINK && conn->type != LE_LINK)
return;
if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags))
return;
encrypt = test_bit(HCI_CONN_ENCRYPT, &conn->flags) ? 0x01 : 0x00;
l2cap_security_cfm(conn, status, encrypt);
if (conn->security_cfm_cb)
conn->security_cfm_cb(conn, status);
}
static inline void hci_proto_encrypt_cfm(struct hci_conn *conn, __u8 status,
__u8 encrypt)
{
if (conn->type != ACL_LINK && conn->type != LE_LINK)
return;
l2cap_security_cfm(conn, status, encrypt);
if (conn->security_cfm_cb)
conn->security_cfm_cb(conn, status);
}
/* ----- HCI callbacks ----- */ /* ----- HCI callbacks ----- */
struct hci_cb { struct hci_cb {
struct list_head list; struct list_head list;
char *name; char *name;
void (*connect_cfm) (struct hci_conn *conn, __u8 status);
void (*disconn_cfm) (struct hci_conn *conn, __u8 status);
void (*security_cfm) (struct hci_conn *conn, __u8 status, void (*security_cfm) (struct hci_conn *conn, __u8 status,
__u8 encrypt); __u8 encrypt);
void (*key_change_cfm) (struct hci_conn *conn, __u8 status); void (*key_change_cfm) (struct hci_conn *conn, __u8 status);
void (*role_switch_cfm) (struct hci_conn *conn, __u8 status, __u8 role); void (*role_switch_cfm) (struct hci_conn *conn, __u8 status, __u8 role);
}; };
static inline void hci_connect_cfm(struct hci_conn *conn, __u8 status)
{
struct hci_cb *cb;
mutex_lock(&hci_cb_list_lock);
list_for_each_entry(cb, &hci_cb_list, list) {
if (cb->connect_cfm)
cb->connect_cfm(conn, status);
}
mutex_unlock(&hci_cb_list_lock);
if (conn->connect_cfm_cb)
conn->connect_cfm_cb(conn, status);
}
static inline void hci_disconn_cfm(struct hci_conn *conn, __u8 reason)
{
struct hci_cb *cb;
mutex_lock(&hci_cb_list_lock);
list_for_each_entry(cb, &hci_cb_list, list) {
if (cb->disconn_cfm)
cb->disconn_cfm(conn, reason);
}
mutex_unlock(&hci_cb_list_lock);
if (conn->disconn_cfm_cb)
conn->disconn_cfm_cb(conn, reason);
}
static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status) static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status)
{ {
struct hci_cb *cb; struct hci_cb *cb;
__u8 encrypt; __u8 encrypt;
hci_proto_auth_cfm(conn, status);
if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags)) if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags))
return; return;
encrypt = test_bit(HCI_CONN_ENCRYPT, &conn->flags) ? 0x01 : 0x00; encrypt = test_bit(HCI_CONN_ENCRYPT, &conn->flags) ? 0x01 : 0x00;
read_lock(&hci_cb_list_lock); mutex_lock(&hci_cb_list_lock);
list_for_each_entry(cb, &hci_cb_list, list) { list_for_each_entry(cb, &hci_cb_list, list) {
if (cb->security_cfm) if (cb->security_cfm)
cb->security_cfm(conn, status, encrypt); cb->security_cfm(conn, status, encrypt);
} }
read_unlock(&hci_cb_list_lock); mutex_unlock(&hci_cb_list_lock);
if (conn->security_cfm_cb)
conn->security_cfm_cb(conn, status);
} }
static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status, static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status,
@ -1178,26 +1130,27 @@ static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status,
if (conn->pending_sec_level > conn->sec_level) if (conn->pending_sec_level > conn->sec_level)
conn->sec_level = conn->pending_sec_level; conn->sec_level = conn->pending_sec_level;
hci_proto_encrypt_cfm(conn, status, encrypt); mutex_lock(&hci_cb_list_lock);
read_lock(&hci_cb_list_lock);
list_for_each_entry(cb, &hci_cb_list, list) { list_for_each_entry(cb, &hci_cb_list, list) {
if (cb->security_cfm) if (cb->security_cfm)
cb->security_cfm(conn, status, encrypt); cb->security_cfm(conn, status, encrypt);
} }
read_unlock(&hci_cb_list_lock); mutex_unlock(&hci_cb_list_lock);
if (conn->security_cfm_cb)
conn->security_cfm_cb(conn, status);
} }
static inline void hci_key_change_cfm(struct hci_conn *conn, __u8 status) static inline void hci_key_change_cfm(struct hci_conn *conn, __u8 status)
{ {
struct hci_cb *cb; struct hci_cb *cb;
read_lock(&hci_cb_list_lock); mutex_lock(&hci_cb_list_lock);
list_for_each_entry(cb, &hci_cb_list, list) { list_for_each_entry(cb, &hci_cb_list, list) {
if (cb->key_change_cfm) if (cb->key_change_cfm)
cb->key_change_cfm(conn, status); cb->key_change_cfm(conn, status);
} }
read_unlock(&hci_cb_list_lock); mutex_unlock(&hci_cb_list_lock);
} }
static inline void hci_role_switch_cfm(struct hci_conn *conn, __u8 status, static inline void hci_role_switch_cfm(struct hci_conn *conn, __u8 status,
@ -1205,12 +1158,12 @@ static inline void hci_role_switch_cfm(struct hci_conn *conn, __u8 status,
{ {
struct hci_cb *cb; struct hci_cb *cb;
read_lock(&hci_cb_list_lock); mutex_lock(&hci_cb_list_lock);
list_for_each_entry(cb, &hci_cb_list, list) { list_for_each_entry(cb, &hci_cb_list, list) {
if (cb->role_switch_cfm) if (cb->role_switch_cfm)
cb->role_switch_cfm(conn, status, role); cb->role_switch_cfm(conn, status, role);
} }
read_unlock(&hci_cb_list_lock); mutex_unlock(&hci_cb_list_lock);
} }
static inline bool eir_has_data_type(u8 *data, size_t data_len, u8 type) static inline bool eir_has_data_type(u8 *data, size_t data_len, u8 type)
@ -1312,7 +1265,8 @@ void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode);
/* ----- HCI Sockets ----- */ /* ----- HCI Sockets ----- */
void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb); void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb);
void hci_send_to_control(struct sk_buff *skb, struct sock *skip_sk); void hci_send_to_channel(unsigned short channel, struct sk_buff *skb,
struct sock *skip_sk);
void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb); void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb);
void hci_sock_dev_event(struct hci_dev *hdev, int event); void hci_sock_dev_event(struct hci_dev *hdev, int event);

View file

@ -647,9 +647,14 @@ struct mgmt_ev_new_irk {
struct mgmt_irk_info irk; struct mgmt_irk_info irk;
} __packed; } __packed;
#define MGMT_CSRK_LOCAL_UNAUTHENTICATED 0x00
#define MGMT_CSRK_REMOTE_UNAUTHENTICATED 0x01
#define MGMT_CSRK_LOCAL_AUTHENTICATED 0x02
#define MGMT_CSRK_REMOTE_AUTHENTICATED 0x03
struct mgmt_csrk_info { struct mgmt_csrk_info {
struct mgmt_addr_info addr; struct mgmt_addr_info addr;
__u8 master; __u8 type;
__u8 val[16]; __u8 val[16];
} __packed; } __packed;

View file

@ -19,6 +19,7 @@
#include <net/af_ieee802154.h> #include <net/af_ieee802154.h>
#include <linux/ieee802154.h> #include <linux/ieee802154.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/unaligned/memmove.h>
#include <net/cfg802154.h> #include <net/cfg802154.h>
@ -233,9 +234,7 @@ struct ieee802154_ops {
*/ */
static inline void ieee802154_be64_to_le64(void *le64_dst, const void *be64_src) static inline void ieee802154_be64_to_le64(void *le64_dst, const void *be64_src)
{ {
__le64 tmp = (__force __le64)swab64p(be64_src); __put_unaligned_memmove64(swab64p(be64_src), le64_dst);
memcpy(le64_dst, &tmp, IEEE802154_EXTENDED_ADDR_LEN);
} }
/** /**
@ -245,9 +244,7 @@ static inline void ieee802154_be64_to_le64(void *le64_dst, const void *be64_src)
*/ */
static inline void ieee802154_le64_to_be64(void *be64_dst, const void *le64_src) static inline void ieee802154_le64_to_be64(void *be64_dst, const void *le64_src)
{ {
__be64 tmp = (__force __be64)swab64p(le64_src); __put_unaligned_memmove64(swab64p(le64_src), be64_dst);
memcpy(be64_dst, &tmp, IEEE802154_EXTENDED_ADDR_LEN);
} }
/* Basic interface to register ieee802154 hwice */ /* Basic interface to register ieee802154 hwice */

View file

@ -1,6 +1,61 @@
config 6LOWPAN menuconfig 6LOWPAN
tristate "6LoWPAN Support" tristate "6LoWPAN Support"
depends on IPV6 depends on IPV6
---help--- ---help---
This enables IPv6 over Low power Wireless Personal Area Network - This enables IPv6 over Low power Wireless Personal Area Network -
"6LoWPAN" which is supported by IEEE 802.15.4 or Bluetooth stacks. "6LoWPAN" which is supported by IEEE 802.15.4 or Bluetooth stacks.
menuconfig 6LOWPAN_NHC
tristate "Next Header Compression Support"
depends on 6LOWPAN
default y
---help---
Support for next header compression.
if 6LOWPAN_NHC
config 6LOWPAN_NHC_DEST
tristate "Destination Options Header Support"
default y
---help---
6LoWPAN IPv6 Destination Options Header compression according to
RFC6282.
config 6LOWPAN_NHC_FRAGMENT
tristate "Fragment Header Support"
default y
---help---
6LoWPAN IPv6 Fragment Header compression according to RFC6282.
config 6LOWPAN_NHC_HOP
tristate "Hop-by-Hop Options Header Support"
default y
---help---
6LoWPAN IPv6 Hop-by-Hop Options Header compression according to
RFC6282.
config 6LOWPAN_NHC_IPV6
tristate "IPv6 Header Support"
default y
---help---
6LoWPAN IPv6 Header compression according to RFC6282.
config 6LOWPAN_NHC_MOBILITY
tristate "Mobility Header Support"
default y
---help---
6LoWPAN IPv6 Mobility Header compression according to RFC6282.
config 6LOWPAN_NHC_ROUTING
tristate "Routing Header Support"
default y
---help---
6LoWPAN IPv6 Routing Header compression according to RFC6282.
config 6LOWPAN_NHC_UDP
tristate "UDP Header Support"
default y
---help---
6LoWPAN IPv6 UDP Header compression according to RFC6282.
endif

View file

@ -1,3 +1,12 @@
obj-$(CONFIG_6LOWPAN) := 6lowpan.o obj-$(CONFIG_6LOWPAN) += 6lowpan.o
6lowpan-y := iphc.o 6lowpan-y := iphc.o nhc.o
#rfc6282 nhcs
obj-$(CONFIG_6LOWPAN_NHC_DEST) += nhc_dest.o
obj-$(CONFIG_6LOWPAN_NHC_FRAGMENT) += nhc_fragment.o
obj-$(CONFIG_6LOWPAN_NHC_HOP) += nhc_hop.o
obj-$(CONFIG_6LOWPAN_NHC_IPV6) += nhc_ipv6.o
obj-$(CONFIG_6LOWPAN_NHC_MOBILITY) += nhc_mobility.o
obj-$(CONFIG_6LOWPAN_NHC_ROUTING) += nhc_routing.o
obj-$(CONFIG_6LOWPAN_NHC_UDP) += nhc_udp.o

View file

@ -54,6 +54,8 @@
#include <net/ipv6.h> #include <net/ipv6.h>
#include <net/af_ieee802154.h> #include <net/af_ieee802154.h>
#include "nhc.h"
/* Uncompress address function for source and /* Uncompress address function for source and
* destination address(non-multicast). * destination address(non-multicast).
* *
@ -224,77 +226,6 @@ static int lowpan_uncompress_multicast_daddr(struct sk_buff *skb,
return 0; return 0;
} }
static int uncompress_udp_header(struct sk_buff *skb, struct udphdr *uh)
{
bool fail;
u8 tmp = 0, val = 0;
fail = lowpan_fetch_skb(skb, &tmp, sizeof(tmp));
if ((tmp & LOWPAN_NHC_UDP_MASK) == LOWPAN_NHC_UDP_ID) {
pr_debug("UDP header uncompression\n");
switch (tmp & LOWPAN_NHC_UDP_CS_P_11) {
case LOWPAN_NHC_UDP_CS_P_00:
fail |= lowpan_fetch_skb(skb, &uh->source,
sizeof(uh->source));
fail |= lowpan_fetch_skb(skb, &uh->dest,
sizeof(uh->dest));
break;
case LOWPAN_NHC_UDP_CS_P_01:
fail |= lowpan_fetch_skb(skb, &uh->source,
sizeof(uh->source));
fail |= lowpan_fetch_skb(skb, &val, sizeof(val));
uh->dest = htons(val + LOWPAN_NHC_UDP_8BIT_PORT);
break;
case LOWPAN_NHC_UDP_CS_P_10:
fail |= lowpan_fetch_skb(skb, &val, sizeof(val));
uh->source = htons(val + LOWPAN_NHC_UDP_8BIT_PORT);
fail |= lowpan_fetch_skb(skb, &uh->dest,
sizeof(uh->dest));
break;
case LOWPAN_NHC_UDP_CS_P_11:
fail |= lowpan_fetch_skb(skb, &val, sizeof(val));
uh->source = htons(LOWPAN_NHC_UDP_4BIT_PORT +
(val >> 4));
uh->dest = htons(LOWPAN_NHC_UDP_4BIT_PORT +
(val & 0x0f));
break;
default:
pr_debug("ERROR: unknown UDP format\n");
goto err;
}
pr_debug("uncompressed UDP ports: src = %d, dst = %d\n",
ntohs(uh->source), ntohs(uh->dest));
/* checksum */
if (tmp & LOWPAN_NHC_UDP_CS_C) {
pr_debug_ratelimited("checksum elided currently not supported\n");
goto err;
} else {
fail |= lowpan_fetch_skb(skb, &uh->check,
sizeof(uh->check));
}
/* UDP length needs to be infered from the lower layers
* here, we obtain the hint from the remaining size of the
* frame
*/
uh->len = htons(skb->len + sizeof(struct udphdr));
pr_debug("uncompressed UDP length: src = %d", ntohs(uh->len));
} else {
pr_debug("ERROR: unsupported NH format\n");
goto err;
}
if (fail)
goto err;
return 0;
err:
return -EINVAL;
}
/* TTL uncompression values */ /* TTL uncompression values */
static const u8 lowpan_ttl_values[] = { 0, 1, 64, 255 }; static const u8 lowpan_ttl_values[] = { 0, 1, 64, 255 };
@ -425,29 +356,11 @@ lowpan_header_decompress(struct sk_buff *skb, struct net_device *dev,
return -EINVAL; return -EINVAL;
} }
/* UDP data uncompression */ /* Next header data uncompression */
if (iphc0 & LOWPAN_IPHC_NH_C) { if (iphc0 & LOWPAN_IPHC_NH_C) {
struct udphdr uh; err = lowpan_nhc_do_uncompression(skb, dev, &hdr);
const int needed = sizeof(struct udphdr) + sizeof(hdr); if (err < 0)
if (uncompress_udp_header(skb, &uh))
return -EINVAL;
/* replace the compressed UDP head by the uncompressed UDP
* header
*/
err = skb_cow(skb, needed);
if (unlikely(err))
return err; return err;
skb_push(skb, sizeof(struct udphdr));
skb_reset_transport_header(skb);
skb_copy_to_linear_data(skb, &uh, sizeof(struct udphdr));
raw_dump_table(__func__, "raw UDP header dump",
(u8 *)&uh, sizeof(uh));
hdr.nexthdr = UIP_PROTO_UDP;
} else { } else {
err = skb_cow(skb, sizeof(hdr)); err = skb_cow(skb, sizeof(hdr));
if (unlikely(err)) if (unlikely(err))
@ -500,71 +413,6 @@ static u8 lowpan_compress_addr_64(u8 **hc_ptr, u8 shift,
return rol8(val, shift); return rol8(val, shift);
} }
static void compress_udp_header(u8 **hc_ptr, struct sk_buff *skb)
{
struct udphdr *uh;
u8 tmp;
/* In the case of RAW sockets the transport header is not set by
* the ip6 stack so we must set it ourselves
*/
if (skb->transport_header == skb->network_header)
skb_set_transport_header(skb, sizeof(struct ipv6hdr));
uh = udp_hdr(skb);
if (((ntohs(uh->source) & LOWPAN_NHC_UDP_4BIT_MASK) ==
LOWPAN_NHC_UDP_4BIT_PORT) &&
((ntohs(uh->dest) & LOWPAN_NHC_UDP_4BIT_MASK) ==
LOWPAN_NHC_UDP_4BIT_PORT)) {
pr_debug("UDP header: both ports compression to 4 bits\n");
/* compression value */
tmp = LOWPAN_NHC_UDP_CS_P_11;
lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
/* source and destination port */
tmp = ntohs(uh->dest) - LOWPAN_NHC_UDP_4BIT_PORT +
((ntohs(uh->source) - LOWPAN_NHC_UDP_4BIT_PORT) << 4);
lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
} else if ((ntohs(uh->dest) & LOWPAN_NHC_UDP_8BIT_MASK) ==
LOWPAN_NHC_UDP_8BIT_PORT) {
pr_debug("UDP header: remove 8 bits of dest\n");
/* compression value */
tmp = LOWPAN_NHC_UDP_CS_P_01;
lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
/* source port */
lowpan_push_hc_data(hc_ptr, &uh->source, sizeof(uh->source));
/* destination port */
tmp = ntohs(uh->dest) - LOWPAN_NHC_UDP_8BIT_PORT;
lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
} else if ((ntohs(uh->source) & LOWPAN_NHC_UDP_8BIT_MASK) ==
LOWPAN_NHC_UDP_8BIT_PORT) {
pr_debug("UDP header: remove 8 bits of source\n");
/* compression value */
tmp = LOWPAN_NHC_UDP_CS_P_10;
lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
/* source port */
tmp = ntohs(uh->source) - LOWPAN_NHC_UDP_8BIT_PORT;
lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
/* destination port */
lowpan_push_hc_data(hc_ptr, &uh->dest, sizeof(uh->dest));
} else {
pr_debug("UDP header: can't compress\n");
/* compression value */
tmp = LOWPAN_NHC_UDP_CS_P_00;
lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
/* source port */
lowpan_push_hc_data(hc_ptr, &uh->source, sizeof(uh->source));
/* destination port */
lowpan_push_hc_data(hc_ptr, &uh->dest, sizeof(uh->dest));
}
/* checksum is always inline */
lowpan_push_hc_data(hc_ptr, &uh->check, sizeof(uh->check));
/* skip the UDP header */
skb_pull(skb, sizeof(struct udphdr));
}
int lowpan_header_compress(struct sk_buff *skb, struct net_device *dev, int lowpan_header_compress(struct sk_buff *skb, struct net_device *dev,
unsigned short type, const void *_daddr, unsigned short type, const void *_daddr,
const void *_saddr, unsigned int len) const void *_saddr, unsigned int len)
@ -572,7 +420,7 @@ int lowpan_header_compress(struct sk_buff *skb, struct net_device *dev,
u8 tmp, iphc0, iphc1, *hc_ptr; u8 tmp, iphc0, iphc1, *hc_ptr;
struct ipv6hdr *hdr; struct ipv6hdr *hdr;
u8 head[100] = {}; u8 head[100] = {};
int addr_type; int ret, addr_type;
if (type != ETH_P_IPV6) if (type != ETH_P_IPV6)
return -EINVAL; return -EINVAL;
@ -649,13 +497,12 @@ int lowpan_header_compress(struct sk_buff *skb, struct net_device *dev,
/* NOTE: payload length is always compressed */ /* NOTE: payload length is always compressed */
/* Next Header is compress if UDP */ /* Check if we provide the nhc format for nexthdr and compression
if (hdr->nexthdr == UIP_PROTO_UDP) * functionality. If not nexthdr is handled inline and not compressed.
iphc0 |= LOWPAN_IPHC_NH_C; */
ret = lowpan_nhc_check_compression(skb, hdr, &hc_ptr, &iphc0);
if ((iphc0 & LOWPAN_IPHC_NH_C) == 0) if (ret < 0)
lowpan_push_hc_data(&hc_ptr, &hdr->nexthdr, return ret;
sizeof(hdr->nexthdr));
/* Hop limit /* Hop limit
* if 1: compress, encoding is 01 * if 1: compress, encoding is 01
@ -741,9 +588,12 @@ int lowpan_header_compress(struct sk_buff *skb, struct net_device *dev,
} }
} }
/* UDP header compression */ /* next header compression */
if (hdr->nexthdr == UIP_PROTO_UDP) if (iphc0 & LOWPAN_IPHC_NH_C) {
compress_udp_header(&hc_ptr, skb); ret = lowpan_nhc_do_compression(skb, hdr, &hc_ptr);
if (ret < 0)
return ret;
}
head[0] = iphc0; head[0] = iphc0;
head[1] = iphc1; head[1] = iphc1;
@ -761,4 +611,18 @@ int lowpan_header_compress(struct sk_buff *skb, struct net_device *dev,
} }
EXPORT_SYMBOL_GPL(lowpan_header_compress); EXPORT_SYMBOL_GPL(lowpan_header_compress);
static int __init lowpan_module_init(void)
{
request_module_nowait("nhc_dest");
request_module_nowait("nhc_fragment");
request_module_nowait("nhc_hop");
request_module_nowait("nhc_ipv6");
request_module_nowait("nhc_mobility");
request_module_nowait("nhc_routing");
request_module_nowait("nhc_udp");
return 0;
}
module_init(lowpan_module_init);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");

241
net/6lowpan/nhc.c Normal file
View file

@ -0,0 +1,241 @@
/*
* 6LoWPAN next header compression
*
*
* Authors:
* Alexander Aring <aar@pengutronix.de>
*
* 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.
*/
#include <linux/netdevice.h>
#include <net/ipv6.h>
#include "nhc.h"
static struct rb_root rb_root = RB_ROOT;
static struct lowpan_nhc *lowpan_nexthdr_nhcs[NEXTHDR_MAX];
static DEFINE_SPINLOCK(lowpan_nhc_lock);
static int lowpan_nhc_insert(struct lowpan_nhc *nhc)
{
struct rb_node **new = &rb_root.rb_node, *parent = NULL;
/* Figure out where to put new node */
while (*new) {
struct lowpan_nhc *this = container_of(*new, struct lowpan_nhc,
node);
int result, len_dif, len;
len_dif = nhc->idlen - this->idlen;
if (nhc->idlen < this->idlen)
len = nhc->idlen;
else
len = this->idlen;
result = memcmp(nhc->id, this->id, len);
if (!result)
result = len_dif;
parent = *new;
if (result < 0)
new = &((*new)->rb_left);
else if (result > 0)
new = &((*new)->rb_right);
else
return -EEXIST;
}
/* Add new node and rebalance tree. */
rb_link_node(&nhc->node, parent, new);
rb_insert_color(&nhc->node, &rb_root);
return 0;
}
static void lowpan_nhc_remove(struct lowpan_nhc *nhc)
{
rb_erase(&nhc->node, &rb_root);
}
static struct lowpan_nhc *lowpan_nhc_by_nhcid(const struct sk_buff *skb)
{
struct rb_node *node = rb_root.rb_node;
const u8 *nhcid_skb_ptr = skb->data;
while (node) {
struct lowpan_nhc *nhc = container_of(node, struct lowpan_nhc,
node);
u8 nhcid_skb_ptr_masked[LOWPAN_NHC_MAX_ID_LEN];
int result, i;
if (nhcid_skb_ptr + nhc->idlen > skb->data + skb->len)
return NULL;
/* copy and mask afterwards the nhid value from skb */
memcpy(nhcid_skb_ptr_masked, nhcid_skb_ptr, nhc->idlen);
for (i = 0; i < nhc->idlen; i++)
nhcid_skb_ptr_masked[i] &= nhc->idmask[i];
result = memcmp(nhcid_skb_ptr_masked, nhc->id, nhc->idlen);
if (result < 0)
node = node->rb_left;
else if (result > 0)
node = node->rb_right;
else
return nhc;
}
return NULL;
}
int lowpan_nhc_check_compression(struct sk_buff *skb,
const struct ipv6hdr *hdr, u8 **hc_ptr,
u8 *iphc0)
{
struct lowpan_nhc *nhc;
spin_lock_bh(&lowpan_nhc_lock);
nhc = lowpan_nexthdr_nhcs[hdr->nexthdr];
if (nhc && nhc->compress)
*iphc0 |= LOWPAN_IPHC_NH_C;
else
lowpan_push_hc_data(hc_ptr, &hdr->nexthdr,
sizeof(hdr->nexthdr));
spin_unlock_bh(&lowpan_nhc_lock);
return 0;
}
int lowpan_nhc_do_compression(struct sk_buff *skb, const struct ipv6hdr *hdr,
u8 **hc_ptr)
{
int ret;
struct lowpan_nhc *nhc;
spin_lock_bh(&lowpan_nhc_lock);
nhc = lowpan_nexthdr_nhcs[hdr->nexthdr];
/* check if the nhc module was removed in unlocked part.
* TODO: this is a workaround we should prevent unloading
* of nhc modules while unlocked part, this will always drop
* the lowpan packet but it's very unlikely.
*
* Solution isn't easy because we need to decide at
* lowpan_nhc_check_compression if we do a compression or not.
* Because the inline data which is added to skb, we can't move this
* handling.
*/
if (unlikely(!nhc || !nhc->compress)) {
ret = -EINVAL;
goto out;
}
/* In the case of RAW sockets the transport header is not set by
* the ip6 stack so we must set it ourselves
*/
if (skb->transport_header == skb->network_header)
skb_set_transport_header(skb, sizeof(struct ipv6hdr));
ret = nhc->compress(skb, hc_ptr);
if (ret < 0)
goto out;
/* skip the transport header */
skb_pull(skb, nhc->nexthdrlen);
out:
spin_unlock_bh(&lowpan_nhc_lock);
return ret;
}
int lowpan_nhc_do_uncompression(struct sk_buff *skb, struct net_device *dev,
struct ipv6hdr *hdr)
{
struct lowpan_nhc *nhc;
int ret;
spin_lock_bh(&lowpan_nhc_lock);
nhc = lowpan_nhc_by_nhcid(skb);
if (nhc) {
if (nhc->uncompress) {
ret = nhc->uncompress(skb, sizeof(struct ipv6hdr) +
nhc->nexthdrlen);
if (ret < 0) {
spin_unlock_bh(&lowpan_nhc_lock);
return ret;
}
} else {
spin_unlock_bh(&lowpan_nhc_lock);
netdev_warn(dev, "received nhc id for %s which is not implemented.\n",
nhc->name);
return -ENOTSUPP;
}
} else {
spin_unlock_bh(&lowpan_nhc_lock);
netdev_warn(dev, "received unknown nhc id which was not found.\n");
return -ENOENT;
}
hdr->nexthdr = nhc->nexthdr;
skb_reset_transport_header(skb);
raw_dump_table(__func__, "raw transport header dump",
skb_transport_header(skb), nhc->nexthdrlen);
spin_unlock_bh(&lowpan_nhc_lock);
return 0;
}
int lowpan_nhc_add(struct lowpan_nhc *nhc)
{
int ret;
if (!nhc->idlen || !nhc->idsetup)
return -EINVAL;
WARN_ONCE(nhc->idlen > LOWPAN_NHC_MAX_ID_LEN,
"LOWPAN_NHC_MAX_ID_LEN should be updated to %zd.\n",
nhc->idlen);
nhc->idsetup(nhc);
spin_lock_bh(&lowpan_nhc_lock);
if (lowpan_nexthdr_nhcs[nhc->nexthdr]) {
ret = -EEXIST;
goto out;
}
ret = lowpan_nhc_insert(nhc);
if (ret < 0)
goto out;
lowpan_nexthdr_nhcs[nhc->nexthdr] = nhc;
out:
spin_unlock_bh(&lowpan_nhc_lock);
return ret;
}
EXPORT_SYMBOL(lowpan_nhc_add);
void lowpan_nhc_del(struct lowpan_nhc *nhc)
{
spin_lock_bh(&lowpan_nhc_lock);
lowpan_nhc_remove(nhc);
lowpan_nexthdr_nhcs[nhc->nexthdr] = NULL;
spin_unlock_bh(&lowpan_nhc_lock);
synchronize_net();
}
EXPORT_SYMBOL(lowpan_nhc_del);

146
net/6lowpan/nhc.h Normal file
View file

@ -0,0 +1,146 @@
#ifndef __6LOWPAN_NHC_H
#define __6LOWPAN_NHC_H
#include <linux/skbuff.h>
#include <linux/rbtree.h>
#include <linux/module.h>
#include <net/6lowpan.h>
#include <net/ipv6.h>
#define LOWPAN_NHC_MAX_ID_LEN 1
/**
* LOWPAN_NHC - helper macro to generate nh id fields and lowpan_nhc struct
*
* @__nhc: variable name of the lowpan_nhc struct.
* @_name: const char * of common header compression name.
* @_nexthdr: ipv6 nexthdr field for the header compression.
* @_nexthdrlen: ipv6 nexthdr len for the reserved space.
* @_idsetup: callback to setup id and mask values.
* @_idlen: len for the next header id and mask, should be always the same.
* @_uncompress: callback for uncompression call.
* @_compress: callback for compression call.
*/
#define LOWPAN_NHC(__nhc, _name, _nexthdr, \
_hdrlen, _idsetup, _idlen, \
_uncompress, _compress) \
static u8 __nhc##_val[_idlen]; \
static u8 __nhc##_mask[_idlen]; \
static struct lowpan_nhc __nhc = { \
.name = _name, \
.nexthdr = _nexthdr, \
.nexthdrlen = _hdrlen, \
.id = __nhc##_val, \
.idmask = __nhc##_mask, \
.idlen = _idlen, \
.idsetup = _idsetup, \
.uncompress = _uncompress, \
.compress = _compress, \
}
#define module_lowpan_nhc(__nhc) \
static int __init __nhc##_init(void) \
{ \
return lowpan_nhc_add(&(__nhc)); \
} \
module_init(__nhc##_init); \
static void __exit __nhc##_exit(void) \
{ \
lowpan_nhc_del(&(__nhc)); \
} \
module_exit(__nhc##_exit);
/**
* struct lowpan_nhc - hold 6lowpan next hdr compression ifnformation
*
* @node: holder for the rbtree.
* @name: name of the specific next header compression
* @nexthdr: next header value of the protocol which should be compressed.
* @nexthdrlen: ipv6 nexthdr len for the reserved space.
* @id: array for nhc id. Note this need to be in network byteorder.
* @mask: array for nhc id mask. Note this need to be in network byteorder.
* @len: the length of the next header id and mask.
* @setup: callback to setup fill the next header id value and mask.
* @compress: callback to do the header compression.
* @uncompress: callback to do the header uncompression.
*/
struct lowpan_nhc {
struct rb_node node;
const char *name;
const u8 nexthdr;
const size_t nexthdrlen;
u8 *id;
u8 *idmask;
const size_t idlen;
void (*idsetup)(struct lowpan_nhc *nhc);
int (*uncompress)(struct sk_buff *skb, size_t needed);
int (*compress)(struct sk_buff *skb, u8 **hc_ptr);
};
/**
* lowpan_nhc_by_nexthdr - return the 6lowpan nhc by ipv6 nexthdr.
*
* @nexthdr: ipv6 nexthdr value.
*/
struct lowpan_nhc *lowpan_nhc_by_nexthdr(u8 nexthdr);
/**
* lowpan_nhc_check_compression - checks if we support compression format. If
* we support the nhc by nexthdr field, the 6LoWPAN iphc NHC bit will be
* set. If we don't support nexthdr will be added as inline data to the
* 6LoWPAN header.
*
* @skb: skb of 6LoWPAN header to read nhc and replace header.
* @hdr: ipv6hdr to check the nexthdr value
* @hc_ptr: pointer for 6LoWPAN header which should increment at the end of
* replaced header.
* @iphc0: iphc0 pointer to set the 6LoWPAN NHC bit
*/
int lowpan_nhc_check_compression(struct sk_buff *skb,
const struct ipv6hdr *hdr, u8 **hc_ptr,
u8 *iphc0);
/**
* lowpan_nhc_do_compression - calling compress callback for nhc
*
* @skb: skb of 6LoWPAN header to read nhc and replace header.
* @hdr: ipv6hdr to set the nexthdr value
* @hc_ptr: pointer for 6LoWPAN header which should increment at the end of
* replaced header.
*/
int lowpan_nhc_do_compression(struct sk_buff *skb, const struct ipv6hdr *hdr,
u8 **hc_ptr);
/**
* lowpan_nhc_do_uncompression - calling uncompress callback for nhc
*
* @nhc: 6LoWPAN nhc context, get by lowpan_nhc_by_ functions.
* @skb: skb of 6LoWPAN header, skb->data should be pointed to nhc id value.
* @dev: netdevice for print logging information.
* @hdr: ipv6hdr for setting nexthdr value.
*/
int lowpan_nhc_do_uncompression(struct sk_buff *skb, struct net_device *dev,
struct ipv6hdr *hdr);
/**
* lowpan_nhc_add - register a next header compression to framework
*
* @nhc: nhc which should be add.
*/
int lowpan_nhc_add(struct lowpan_nhc *nhc);
/**
* lowpan_nhc_del - delete a next header compression from framework
*
* @nhc: nhc which should be delete.
*/
void lowpan_nhc_del(struct lowpan_nhc *nhc);
/**
* lowpan_nhc_init - adding all default nhcs
*/
void lowpan_nhc_init(void);
#endif /* __6LOWPAN_NHC_H */

28
net/6lowpan/nhc_dest.c Normal file
View file

@ -0,0 +1,28 @@
/*
* 6LoWPAN IPv6 Destination Options Header compression according to
* RFC6282
*
* 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.
*/
#include "nhc.h"
#define LOWPAN_NHC_DEST_IDLEN 1
#define LOWPAN_NHC_DEST_ID_0 0xe6
#define LOWPAN_NHC_DEST_MASK_0 0xfe
static void dest_nhid_setup(struct lowpan_nhc *nhc)
{
nhc->id[0] = LOWPAN_NHC_DEST_ID_0;
nhc->idmask[0] = LOWPAN_NHC_DEST_MASK_0;
}
LOWPAN_NHC(nhc_dest, "RFC6282 Destination Options", NEXTHDR_DEST, 0,
dest_nhid_setup, LOWPAN_NHC_DEST_IDLEN, NULL, NULL);
module_lowpan_nhc(nhc_dest);
MODULE_DESCRIPTION("6LoWPAN next header RFC6282 Destination Options compression");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,27 @@
/*
* 6LoWPAN IPv6 Fragment Header compression according to RFC6282
*
* 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.
*/
#include "nhc.h"
#define LOWPAN_NHC_FRAGMENT_IDLEN 1
#define LOWPAN_NHC_FRAGMENT_ID_0 0xe4
#define LOWPAN_NHC_FRAGMENT_MASK_0 0xfe
static void fragment_nhid_setup(struct lowpan_nhc *nhc)
{
nhc->id[0] = LOWPAN_NHC_FRAGMENT_ID_0;
nhc->idmask[0] = LOWPAN_NHC_FRAGMENT_MASK_0;
}
LOWPAN_NHC(nhc_fragment, "RFC6282 Fragment", NEXTHDR_FRAGMENT, 0,
fragment_nhid_setup, LOWPAN_NHC_FRAGMENT_IDLEN, NULL, NULL);
module_lowpan_nhc(nhc_fragment);
MODULE_DESCRIPTION("6LoWPAN next header RFC6282 Fragment compression");
MODULE_LICENSE("GPL");

27
net/6lowpan/nhc_hop.c Normal file
View file

@ -0,0 +1,27 @@
/*
* 6LoWPAN IPv6 Hop-by-Hop Options Header compression according to RFC6282
*
* 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.
*/
#include "nhc.h"
#define LOWPAN_NHC_HOP_IDLEN 1
#define LOWPAN_NHC_HOP_ID_0 0xe0
#define LOWPAN_NHC_HOP_MASK_0 0xfe
static void hop_nhid_setup(struct lowpan_nhc *nhc)
{
nhc->id[0] = LOWPAN_NHC_HOP_ID_0;
nhc->idmask[0] = LOWPAN_NHC_HOP_MASK_0;
}
LOWPAN_NHC(nhc_hop, "RFC6282 Hop-by-Hop Options", NEXTHDR_HOP, 0,
hop_nhid_setup, LOWPAN_NHC_HOP_IDLEN, NULL, NULL);
module_lowpan_nhc(nhc_hop);
MODULE_DESCRIPTION("6LoWPAN next header RFC6282 Hop-by-Hop Options compression");
MODULE_LICENSE("GPL");

27
net/6lowpan/nhc_ipv6.c Normal file
View file

@ -0,0 +1,27 @@
/*
* 6LoWPAN IPv6 Header compression according to RFC6282
*
* 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.
*/
#include "nhc.h"
#define LOWPAN_NHC_IPV6_IDLEN 1
#define LOWPAN_NHC_IPV6_ID_0 0xee
#define LOWPAN_NHC_IPV6_MASK_0 0xfe
static void ipv6_nhid_setup(struct lowpan_nhc *nhc)
{
nhc->id[0] = LOWPAN_NHC_IPV6_ID_0;
nhc->idmask[0] = LOWPAN_NHC_IPV6_MASK_0;
}
LOWPAN_NHC(nhc_ipv6, "RFC6282 IPv6", NEXTHDR_IPV6, 0, ipv6_nhid_setup,
LOWPAN_NHC_IPV6_IDLEN, NULL, NULL);
module_lowpan_nhc(nhc_ipv6);
MODULE_DESCRIPTION("6LoWPAN next header RFC6282 IPv6 compression");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,27 @@
/*
* 6LoWPAN IPv6 Mobility Header compression according to RFC6282
*
* 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.
*/
#include "nhc.h"
#define LOWPAN_NHC_MOBILITY_IDLEN 1
#define LOWPAN_NHC_MOBILITY_ID_0 0xe8
#define LOWPAN_NHC_MOBILITY_MASK_0 0xfe
static void mobility_nhid_setup(struct lowpan_nhc *nhc)
{
nhc->id[0] = LOWPAN_NHC_MOBILITY_ID_0;
nhc->idmask[0] = LOWPAN_NHC_MOBILITY_MASK_0;
}
LOWPAN_NHC(nhc_mobility, "RFC6282 Mobility", NEXTHDR_MOBILITY, 0,
mobility_nhid_setup, LOWPAN_NHC_MOBILITY_IDLEN, NULL, NULL);
module_lowpan_nhc(nhc_mobility);
MODULE_DESCRIPTION("6LoWPAN next header RFC6282 Mobility compression");
MODULE_LICENSE("GPL");

27
net/6lowpan/nhc_routing.c Normal file
View file

@ -0,0 +1,27 @@
/*
* 6LoWPAN IPv6 Routing Header compression according to RFC6282
*
* 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.
*/
#include "nhc.h"
#define LOWPAN_NHC_ROUTING_IDLEN 1
#define LOWPAN_NHC_ROUTING_ID_0 0xe2
#define LOWPAN_NHC_ROUTING_MASK_0 0xfe
static void routing_nhid_setup(struct lowpan_nhc *nhc)
{
nhc->id[0] = LOWPAN_NHC_ROUTING_ID_0;
nhc->idmask[0] = LOWPAN_NHC_ROUTING_MASK_0;
}
LOWPAN_NHC(nhc_routing, "RFC6282 Routing", NEXTHDR_ROUTING, 0,
routing_nhid_setup, LOWPAN_NHC_ROUTING_IDLEN, NULL, NULL);
module_lowpan_nhc(nhc_routing);
MODULE_DESCRIPTION("6LoWPAN next header RFC6282 Routing compression");
MODULE_LICENSE("GPL");

157
net/6lowpan/nhc_udp.c Normal file
View file

@ -0,0 +1,157 @@
/*
* 6LoWPAN IPv6 UDP compression according to RFC6282
*
*
* Authors:
* Alexander Aring <aar@pengutronix.de>
*
* Orignal written by:
* Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
* Jon Smirl <jonsmirl@gmail.com>
*
* 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.
*/
#include "nhc.h"
#define LOWPAN_NHC_UDP_IDLEN 1
static int udp_uncompress(struct sk_buff *skb, size_t needed)
{
u8 tmp = 0, val = 0;
struct udphdr uh;
bool fail;
int err;
fail = lowpan_fetch_skb(skb, &tmp, sizeof(tmp));
pr_debug("UDP header uncompression\n");
switch (tmp & LOWPAN_NHC_UDP_CS_P_11) {
case LOWPAN_NHC_UDP_CS_P_00:
fail |= lowpan_fetch_skb(skb, &uh.source, sizeof(uh.source));
fail |= lowpan_fetch_skb(skb, &uh.dest, sizeof(uh.dest));
break;
case LOWPAN_NHC_UDP_CS_P_01:
fail |= lowpan_fetch_skb(skb, &uh.source, sizeof(uh.source));
fail |= lowpan_fetch_skb(skb, &val, sizeof(val));
uh.dest = htons(val + LOWPAN_NHC_UDP_8BIT_PORT);
break;
case LOWPAN_NHC_UDP_CS_P_10:
fail |= lowpan_fetch_skb(skb, &val, sizeof(val));
uh.source = htons(val + LOWPAN_NHC_UDP_8BIT_PORT);
fail |= lowpan_fetch_skb(skb, &uh.dest, sizeof(uh.dest));
break;
case LOWPAN_NHC_UDP_CS_P_11:
fail |= lowpan_fetch_skb(skb, &val, sizeof(val));
uh.source = htons(LOWPAN_NHC_UDP_4BIT_PORT + (val >> 4));
uh.dest = htons(LOWPAN_NHC_UDP_4BIT_PORT + (val & 0x0f));
break;
default:
BUG();
}
pr_debug("uncompressed UDP ports: src = %d, dst = %d\n",
ntohs(uh.source), ntohs(uh.dest));
/* checksum */
if (tmp & LOWPAN_NHC_UDP_CS_C) {
pr_debug_ratelimited("checksum elided currently not supported\n");
fail = true;
} else {
fail |= lowpan_fetch_skb(skb, &uh.check, sizeof(uh.check));
}
if (fail)
return -EINVAL;
/* UDP length needs to be infered from the lower layers
* here, we obtain the hint from the remaining size of the
* frame
*/
uh.len = htons(skb->len + sizeof(struct udphdr));
pr_debug("uncompressed UDP length: src = %d", ntohs(uh.len));
/* replace the compressed UDP head by the uncompressed UDP
* header
*/
err = skb_cow(skb, needed);
if (unlikely(err))
return err;
skb_push(skb, sizeof(struct udphdr));
skb_copy_to_linear_data(skb, &uh, sizeof(struct udphdr));
return 0;
}
static int udp_compress(struct sk_buff *skb, u8 **hc_ptr)
{
const struct udphdr *uh = udp_hdr(skb);
u8 tmp;
if (((ntohs(uh->source) & LOWPAN_NHC_UDP_4BIT_MASK) ==
LOWPAN_NHC_UDP_4BIT_PORT) &&
((ntohs(uh->dest) & LOWPAN_NHC_UDP_4BIT_MASK) ==
LOWPAN_NHC_UDP_4BIT_PORT)) {
pr_debug("UDP header: both ports compression to 4 bits\n");
/* compression value */
tmp = LOWPAN_NHC_UDP_CS_P_11;
lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
/* source and destination port */
tmp = ntohs(uh->dest) - LOWPAN_NHC_UDP_4BIT_PORT +
((ntohs(uh->source) - LOWPAN_NHC_UDP_4BIT_PORT) << 4);
lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
} else if ((ntohs(uh->dest) & LOWPAN_NHC_UDP_8BIT_MASK) ==
LOWPAN_NHC_UDP_8BIT_PORT) {
pr_debug("UDP header: remove 8 bits of dest\n");
/* compression value */
tmp = LOWPAN_NHC_UDP_CS_P_01;
lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
/* source port */
lowpan_push_hc_data(hc_ptr, &uh->source, sizeof(uh->source));
/* destination port */
tmp = ntohs(uh->dest) - LOWPAN_NHC_UDP_8BIT_PORT;
lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
} else if ((ntohs(uh->source) & LOWPAN_NHC_UDP_8BIT_MASK) ==
LOWPAN_NHC_UDP_8BIT_PORT) {
pr_debug("UDP header: remove 8 bits of source\n");
/* compression value */
tmp = LOWPAN_NHC_UDP_CS_P_10;
lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
/* source port */
tmp = ntohs(uh->source) - LOWPAN_NHC_UDP_8BIT_PORT;
lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
/* destination port */
lowpan_push_hc_data(hc_ptr, &uh->dest, sizeof(uh->dest));
} else {
pr_debug("UDP header: can't compress\n");
/* compression value */
tmp = LOWPAN_NHC_UDP_CS_P_00;
lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
/* source port */
lowpan_push_hc_data(hc_ptr, &uh->source, sizeof(uh->source));
/* destination port */
lowpan_push_hc_data(hc_ptr, &uh->dest, sizeof(uh->dest));
}
/* checksum is always inline */
lowpan_push_hc_data(hc_ptr, &uh->check, sizeof(uh->check));
return 0;
}
static void udp_nhid_setup(struct lowpan_nhc *nhc)
{
nhc->id[0] = LOWPAN_NHC_UDP_ID;
nhc->idmask[0] = LOWPAN_NHC_UDP_MASK;
}
LOWPAN_NHC(nhc_udp, "RFC6282 UDP", NEXTHDR_UDP, sizeof(struct udphdr),
udp_nhid_setup, LOWPAN_NHC_UDP_IDLEN, udp_uncompress, udp_compress);
module_lowpan_nhc(nhc_udp);
MODULE_DESCRIPTION("6LoWPAN next header RFC6282 UDP compression");
MODULE_LICENSE("GPL");

View file

@ -91,4 +91,12 @@ config BT_SELFTEST_SMP
Run test cases for SMP cryptographic functionality, including both Run test cases for SMP cryptographic functionality, including both
legacy SMP as well as the Secure Connections features. legacy SMP as well as the Secure Connections features.
config BT_DEBUGFS
bool "Export Bluetooth internals in debugfs"
depends on BT && DEBUG_FS
default y
help
Provide extensive information about internal Bluetooth states
in debugfs.
source "drivers/bluetooth/Kconfig" source "drivers/bluetooth/Kconfig"

View file

@ -13,8 +13,9 @@ bluetooth_6lowpan-y := 6lowpan.o
bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \ bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \
hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o \ hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o \
a2mp.o amp.o ecc.o hci_request.o hci_debugfs.o a2mp.o amp.o ecc.o hci_request.o
bluetooth-$(CONFIG_BT_DEBUGFS) += hci_debugfs.o
bluetooth-$(CONFIG_BT_SELFTEST) += selftest.o bluetooth-$(CONFIG_BT_SELFTEST) += selftest.o
subdir-ccflags-y += -D__CHECK_ENDIAN__ subdir-ccflags-y += -D__CHECK_ENDIAN__

View file

@ -19,9 +19,11 @@
#include "a2mp.h" #include "a2mp.h"
#include "amp.h" #include "amp.h"
#define A2MP_FEAT_EXT 0x8000
/* Global AMP Manager list */ /* Global AMP Manager list */
LIST_HEAD(amp_mgr_list); static LIST_HEAD(amp_mgr_list);
DEFINE_MUTEX(amp_mgr_list_lock); static DEFINE_MUTEX(amp_mgr_list_lock);
/* A2MP build & send command helper functions */ /* A2MP build & send command helper functions */
static struct a2mp_cmd *__a2mp_build(u8 code, u8 ident, u16 len, void *data) static struct a2mp_cmd *__a2mp_build(u8 code, u8 ident, u16 len, void *data)
@ -43,7 +45,7 @@ static struct a2mp_cmd *__a2mp_build(u8 code, u8 ident, u16 len, void *data)
return cmd; return cmd;
} }
void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, void *data) static void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, void *data)
{ {
struct l2cap_chan *chan = mgr->a2mp_chan; struct l2cap_chan *chan = mgr->a2mp_chan;
struct a2mp_cmd *cmd; struct a2mp_cmd *cmd;
@ -67,7 +69,7 @@ void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, void *data)
kfree(cmd); kfree(cmd);
} }
u8 __next_ident(struct amp_mgr *mgr) static u8 __next_ident(struct amp_mgr *mgr)
{ {
if (++mgr->ident == 0) if (++mgr->ident == 0)
mgr->ident = 1; mgr->ident = 1;
@ -75,6 +77,23 @@ u8 __next_ident(struct amp_mgr *mgr)
return mgr->ident; return mgr->ident;
} }
static struct amp_mgr *amp_mgr_lookup_by_state(u8 state)
{
struct amp_mgr *mgr;
mutex_lock(&amp_mgr_list_lock);
list_for_each_entry(mgr, &amp_mgr_list, list) {
if (test_and_clear_bit(state, &mgr->state)) {
amp_mgr_get(mgr);
mutex_unlock(&amp_mgr_list_lock);
return mgr;
}
}
mutex_unlock(&amp_mgr_list_lock);
return NULL;
}
/* hci_dev_list shall be locked */ /* hci_dev_list shall be locked */
static void __a2mp_add_cl(struct amp_mgr *mgr, struct a2mp_cl *cl) static void __a2mp_add_cl(struct amp_mgr *mgr, struct a2mp_cl *cl)
{ {
@ -860,23 +879,6 @@ struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn,
return mgr->a2mp_chan; return mgr->a2mp_chan;
} }
struct amp_mgr *amp_mgr_lookup_by_state(u8 state)
{
struct amp_mgr *mgr;
mutex_lock(&amp_mgr_list_lock);
list_for_each_entry(mgr, &amp_mgr_list, list) {
if (test_and_clear_bit(state, &mgr->state)) {
amp_mgr_get(mgr);
mutex_unlock(&amp_mgr_list_lock);
return mgr;
}
}
mutex_unlock(&amp_mgr_list_lock);
return NULL;
}
void a2mp_send_getinfo_rsp(struct hci_dev *hdev) void a2mp_send_getinfo_rsp(struct hci_dev *hdev)
{ {
struct amp_mgr *mgr; struct amp_mgr *mgr;

View file

@ -17,8 +17,6 @@
#include <net/bluetooth/l2cap.h> #include <net/bluetooth/l2cap.h>
#define A2MP_FEAT_EXT 0x8000
enum amp_mgr_state { enum amp_mgr_state {
READ_LOC_AMP_INFO, READ_LOC_AMP_INFO,
READ_LOC_AMP_ASSOC, READ_LOC_AMP_ASSOC,
@ -131,16 +129,10 @@ struct a2mp_physlink_rsp {
#define A2MP_STATUS_PHYS_LINK_EXISTS 0x05 #define A2MP_STATUS_PHYS_LINK_EXISTS 0x05
#define A2MP_STATUS_SECURITY_VIOLATION 0x06 #define A2MP_STATUS_SECURITY_VIOLATION 0x06
extern struct list_head amp_mgr_list;
extern struct mutex amp_mgr_list_lock;
struct amp_mgr *amp_mgr_get(struct amp_mgr *mgr); struct amp_mgr *amp_mgr_get(struct amp_mgr *mgr);
int amp_mgr_put(struct amp_mgr *mgr); int amp_mgr_put(struct amp_mgr *mgr);
u8 __next_ident(struct amp_mgr *mgr);
struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn, struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn,
struct sk_buff *skb); struct sk_buff *skb);
struct amp_mgr *amp_mgr_lookup_by_state(u8 state);
void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, void *data);
void a2mp_discover_amp(struct l2cap_chan *chan); void a2mp_discover_amp(struct l2cap_chan *chan);
void a2mp_send_getinfo_rsp(struct hci_dev *hdev); void a2mp_send_getinfo_rsp(struct hci_dev *hdev);
void a2mp_send_getampassoc_rsp(struct hci_dev *hdev, u8 status); void a2mp_send_getampassoc_rsp(struct hci_dev *hdev, u8 status);

View file

@ -309,7 +309,7 @@ void hci_sco_setup(struct hci_conn *conn, __u8 status)
else else
hci_add_sco(sco, conn->handle); hci_add_sco(sco, conn->handle);
} else { } else {
hci_proto_connect_cfm(sco, status); hci_connect_cfm(sco, status);
hci_conn_del(sco); hci_conn_del(sco);
} }
} }
@ -618,7 +618,7 @@ void hci_le_conn_failed(struct hci_conn *conn, u8 status)
mgmt_connect_failed(hdev, &conn->dst, conn->type, conn->dst_type, mgmt_connect_failed(hdev, &conn->dst, conn->type, conn->dst_type,
status); status);
hci_proto_connect_cfm(conn, status); hci_connect_cfm(conn, status);
hci_conn_del(conn); hci_conn_del(conn);
@ -733,6 +733,14 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
struct hci_request req; struct hci_request req;
int err; int err;
/* Let's make sure that le is enabled.*/
if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
if (lmp_le_capable(hdev))
return ERR_PTR(-ECONNREFUSED);
return ERR_PTR(-EOPNOTSUPP);
}
/* Some devices send ATT messages as soon as the physical link is /* Some devices send ATT messages as soon as the physical link is
* established. To be able to handle these ATT messages, the user- * established. To be able to handle these ATT messages, the user-
* space first establishes the connection and then starts the pairing * space first establishes the connection and then starts the pairing
@ -856,8 +864,12 @@ struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst,
{ {
struct hci_conn *acl; struct hci_conn *acl;
if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
if (lmp_bredr_capable(hdev))
return ERR_PTR(-ECONNREFUSED);
return ERR_PTR(-EOPNOTSUPP); return ERR_PTR(-EOPNOTSUPP);
}
acl = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst); acl = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
if (!acl) { if (!acl) {
@ -1139,7 +1151,7 @@ void hci_conn_hash_flush(struct hci_dev *hdev)
list_for_each_entry_safe(c, n, &h->list, list) { list_for_each_entry_safe(c, n, &h->list, list) {
c->state = BT_CLOSED; c->state = BT_CLOSED;
hci_proto_disconn_cfm(c, HCI_ERROR_LOCAL_HOST_TERM); hci_disconn_cfm(c, HCI_ERROR_LOCAL_HOST_TERM);
hci_conn_del(c); hci_conn_del(c);
} }
} }

View file

@ -51,7 +51,7 @@ DEFINE_RWLOCK(hci_dev_list_lock);
/* HCI callback list */ /* HCI callback list */
LIST_HEAD(hci_cb_list); LIST_HEAD(hci_cb_list);
DEFINE_RWLOCK(hci_cb_list_lock); DEFINE_MUTEX(hci_cb_list_lock);
/* HCI ID Numbering */ /* HCI ID Numbering */
static DEFINE_IDA(hci_index_ida); static DEFINE_IDA(hci_index_ida);
@ -390,7 +390,7 @@ static void bredr_init(struct hci_request *req)
hci_req_add(req, HCI_OP_READ_BD_ADDR, 0, NULL); hci_req_add(req, HCI_OP_READ_BD_ADDR, 0, NULL);
} }
static void amp_init(struct hci_request *req) static void amp_init1(struct hci_request *req)
{ {
req->hdev->flow_ctl_mode = HCI_FLOW_CTL_MODE_BLOCK_BASED; req->hdev->flow_ctl_mode = HCI_FLOW_CTL_MODE_BLOCK_BASED;
@ -400,9 +400,6 @@ static void amp_init(struct hci_request *req)
/* Read Local Supported Commands */ /* Read Local Supported Commands */
hci_req_add(req, HCI_OP_READ_LOCAL_COMMANDS, 0, NULL); hci_req_add(req, HCI_OP_READ_LOCAL_COMMANDS, 0, NULL);
/* Read Local Supported Features */
hci_req_add(req, HCI_OP_READ_LOCAL_FEATURES, 0, NULL);
/* Read Local AMP Info */ /* Read Local AMP Info */
hci_req_add(req, HCI_OP_READ_LOCAL_AMP_INFO, 0, NULL); hci_req_add(req, HCI_OP_READ_LOCAL_AMP_INFO, 0, NULL);
@ -416,6 +413,16 @@ static void amp_init(struct hci_request *req)
hci_req_add(req, HCI_OP_READ_LOCATION_DATA, 0, NULL); hci_req_add(req, HCI_OP_READ_LOCATION_DATA, 0, NULL);
} }
static void amp_init2(struct hci_request *req)
{
/* Read Local Supported Features. Not all AMP controllers
* support this so it's placed conditionally in the second
* stage init.
*/
if (req->hdev->commands[14] & 0x20)
hci_req_add(req, HCI_OP_READ_LOCAL_FEATURES, 0, NULL);
}
static void hci_init1_req(struct hci_request *req, unsigned long opt) static void hci_init1_req(struct hci_request *req, unsigned long opt)
{ {
struct hci_dev *hdev = req->hdev; struct hci_dev *hdev = req->hdev;
@ -432,7 +439,7 @@ static void hci_init1_req(struct hci_request *req, unsigned long opt)
break; break;
case HCI_AMP: case HCI_AMP:
amp_init(req); amp_init1(req);
break; break;
default: default:
@ -578,6 +585,9 @@ static void hci_init2_req(struct hci_request *req, unsigned long opt)
{ {
struct hci_dev *hdev = req->hdev; struct hci_dev *hdev = req->hdev;
if (hdev->dev_type == HCI_AMP)
return amp_init2(req);
if (lmp_bredr_capable(hdev)) if (lmp_bredr_capable(hdev))
bredr_setup(req); bredr_setup(req);
else else
@ -896,17 +906,17 @@ static int __hci_init(struct hci_dev *hdev)
&dut_mode_fops); &dut_mode_fops);
} }
/* HCI_BREDR covers both single-mode LE, BR/EDR and dual-mode
* BR/EDR/LE type controllers. AMP controllers only need the
* first stage init.
*/
if (hdev->dev_type != HCI_BREDR)
return 0;
err = __hci_req_sync(hdev, hci_init2_req, 0, HCI_INIT_TIMEOUT); err = __hci_req_sync(hdev, hci_init2_req, 0, HCI_INIT_TIMEOUT);
if (err < 0) if (err < 0)
return err; return err;
/* HCI_BREDR covers both single-mode LE, BR/EDR and dual-mode
* BR/EDR/LE type controllers. AMP controllers only need the
* first two stages of init.
*/
if (hdev->dev_type != HCI_BREDR)
return 0;
err = __hci_req_sync(hdev, hci_init3_req, 0, HCI_INIT_TIMEOUT); err = __hci_req_sync(hdev, hci_init3_req, 0, HCI_INIT_TIMEOUT);
if (err < 0) if (err < 0)
return err; return err;
@ -1591,6 +1601,12 @@ static int hci_dev_do_close(struct hci_dev *hdev)
{ {
BT_DBG("%s %p", hdev->name, hdev); BT_DBG("%s %p", hdev->name, hdev);
if (!test_bit(HCI_UNREGISTER, &hdev->dev_flags)) {
/* Execute vendor specific shutdown routine */
if (hdev->shutdown)
hdev->shutdown(hdev);
}
cancel_delayed_work(&hdev->power_off); cancel_delayed_work(&hdev->power_off);
hci_req_cancel(hdev, ENODEV); hci_req_cancel(hdev, ENODEV);
@ -3448,9 +3464,9 @@ int hci_register_cb(struct hci_cb *cb)
{ {
BT_DBG("%p name %s", cb, cb->name); BT_DBG("%p name %s", cb, cb->name);
write_lock(&hci_cb_list_lock); mutex_lock(&hci_cb_list_lock);
list_add(&cb->list, &hci_cb_list); list_add_tail(&cb->list, &hci_cb_list);
write_unlock(&hci_cb_list_lock); mutex_unlock(&hci_cb_list_lock);
return 0; return 0;
} }
@ -3460,9 +3476,9 @@ int hci_unregister_cb(struct hci_cb *cb)
{ {
BT_DBG("%p name %s", cb, cb->name); BT_DBG("%p name %s", cb, cb->name);
write_lock(&hci_cb_list_lock); mutex_lock(&hci_cb_list_lock);
list_del(&cb->list); list_del(&cb->list);
write_unlock(&hci_cb_list_lock); mutex_unlock(&hci_cb_list_lock);
return 0; return 0;
} }

View file

@ -20,7 +20,29 @@
SOFTWARE IS DISCLAIMED. SOFTWARE IS DISCLAIMED.
*/ */
#if IS_ENABLED(CONFIG_BT_DEBUGFS)
void hci_debugfs_create_common(struct hci_dev *hdev); void hci_debugfs_create_common(struct hci_dev *hdev);
void hci_debugfs_create_bredr(struct hci_dev *hdev); void hci_debugfs_create_bredr(struct hci_dev *hdev);
void hci_debugfs_create_le(struct hci_dev *hdev); void hci_debugfs_create_le(struct hci_dev *hdev);
void hci_debugfs_create_conn(struct hci_conn *conn); void hci_debugfs_create_conn(struct hci_conn *conn);
#else
static inline void hci_debugfs_create_common(struct hci_dev *hdev)
{
}
static inline void hci_debugfs_create_bredr(struct hci_dev *hdev)
{
}
static inline void hci_debugfs_create_le(struct hci_dev *hdev)
{
}
static inline void hci_debugfs_create_conn(struct hci_conn *conn)
{
}
#endif

View file

@ -1537,7 +1537,7 @@ static void hci_cs_create_conn(struct hci_dev *hdev, __u8 status)
if (conn && conn->state == BT_CONNECT) { if (conn && conn->state == BT_CONNECT) {
if (status != 0x0c || conn->attempt > 2) { if (status != 0x0c || conn->attempt > 2) {
conn->state = BT_CLOSED; conn->state = BT_CLOSED;
hci_proto_connect_cfm(conn, status); hci_connect_cfm(conn, status);
hci_conn_del(conn); hci_conn_del(conn);
} else } else
conn->state = BT_CONNECT2; conn->state = BT_CONNECT2;
@ -1581,7 +1581,7 @@ static void hci_cs_add_sco(struct hci_dev *hdev, __u8 status)
if (sco) { if (sco) {
sco->state = BT_CLOSED; sco->state = BT_CLOSED;
hci_proto_connect_cfm(sco, status); hci_connect_cfm(sco, status);
hci_conn_del(sco); hci_conn_del(sco);
} }
} }
@ -1608,7 +1608,7 @@ static void hci_cs_auth_requested(struct hci_dev *hdev, __u8 status)
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
if (conn) { if (conn) {
if (conn->state == BT_CONFIG) { if (conn->state == BT_CONFIG) {
hci_proto_connect_cfm(conn, status); hci_connect_cfm(conn, status);
hci_conn_drop(conn); hci_conn_drop(conn);
} }
} }
@ -1635,7 +1635,7 @@ static void hci_cs_set_conn_encrypt(struct hci_dev *hdev, __u8 status)
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
if (conn) { if (conn) {
if (conn->state == BT_CONFIG) { if (conn->state == BT_CONFIG) {
hci_proto_connect_cfm(conn, status); hci_connect_cfm(conn, status);
hci_conn_drop(conn); hci_conn_drop(conn);
} }
} }
@ -1811,7 +1811,7 @@ static void hci_cs_read_remote_features(struct hci_dev *hdev, __u8 status)
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
if (conn) { if (conn) {
if (conn->state == BT_CONFIG) { if (conn->state == BT_CONFIG) {
hci_proto_connect_cfm(conn, status); hci_connect_cfm(conn, status);
hci_conn_drop(conn); hci_conn_drop(conn);
} }
} }
@ -1838,7 +1838,7 @@ static void hci_cs_read_remote_ext_features(struct hci_dev *hdev, __u8 status)
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
if (conn) { if (conn) {
if (conn->state == BT_CONFIG) { if (conn->state == BT_CONFIG) {
hci_proto_connect_cfm(conn, status); hci_connect_cfm(conn, status);
hci_conn_drop(conn); hci_conn_drop(conn);
} }
} }
@ -1873,7 +1873,7 @@ static void hci_cs_setup_sync_conn(struct hci_dev *hdev, __u8 status)
if (sco) { if (sco) {
sco->state = BT_CLOSED; sco->state = BT_CLOSED;
hci_proto_connect_cfm(sco, status); hci_connect_cfm(sco, status);
hci_conn_del(sco); hci_conn_del(sco);
} }
} }
@ -2255,10 +2255,10 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_sco_setup(conn, ev->status); hci_sco_setup(conn, ev->status);
if (ev->status) { if (ev->status) {
hci_proto_connect_cfm(conn, ev->status); hci_connect_cfm(conn, ev->status);
hci_conn_del(conn); hci_conn_del(conn);
} else if (ev->link_type != ACL_LINK) } else if (ev->link_type != ACL_LINK)
hci_proto_connect_cfm(conn, ev->status); hci_connect_cfm(conn, ev->status);
unlock: unlock:
hci_dev_unlock(hdev); hci_dev_unlock(hdev);
@ -2366,7 +2366,7 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
&cp); &cp);
} else { } else {
conn->state = BT_CONNECT2; conn->state = BT_CONNECT2;
hci_proto_connect_cfm(conn, 0); hci_connect_cfm(conn, 0);
} }
} }
@ -2444,7 +2444,7 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
type = conn->type; type = conn->type;
hci_proto_disconn_cfm(conn, ev->reason); hci_disconn_cfm(conn, ev->reason);
hci_conn_del(conn); hci_conn_del(conn);
/* Re-enable advertising if necessary, since it might /* Re-enable advertising if necessary, since it might
@ -2501,7 +2501,7 @@ static void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
&cp); &cp);
} else { } else {
conn->state = BT_CONNECTED; conn->state = BT_CONNECTED;
hci_proto_connect_cfm(conn, ev->status); hci_connect_cfm(conn, ev->status);
hci_conn_drop(conn); hci_conn_drop(conn);
} }
} else { } else {
@ -2629,12 +2629,12 @@ static void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
if (test_bit(HCI_SC_ONLY, &hdev->dev_flags) && if (test_bit(HCI_SC_ONLY, &hdev->dev_flags) &&
(!test_bit(HCI_CONN_AES_CCM, &conn->flags) || (!test_bit(HCI_CONN_AES_CCM, &conn->flags) ||
conn->key_type != HCI_LK_AUTH_COMBINATION_P256)) { conn->key_type != HCI_LK_AUTH_COMBINATION_P256)) {
hci_proto_connect_cfm(conn, HCI_ERROR_AUTH_FAILURE); hci_connect_cfm(conn, HCI_ERROR_AUTH_FAILURE);
hci_conn_drop(conn); hci_conn_drop(conn);
goto unlock; goto unlock;
} }
hci_proto_connect_cfm(conn, ev->status); hci_connect_cfm(conn, ev->status);
hci_conn_drop(conn); hci_conn_drop(conn);
} else } else
hci_encrypt_cfm(conn, ev->status, ev->encrypt); hci_encrypt_cfm(conn, ev->status, ev->encrypt);
@ -2707,7 +2707,7 @@ static void hci_remote_features_evt(struct hci_dev *hdev,
if (!hci_outgoing_auth_needed(hdev, conn)) { if (!hci_outgoing_auth_needed(hdev, conn)) {
conn->state = BT_CONNECTED; conn->state = BT_CONNECTED;
hci_proto_connect_cfm(conn, ev->status); hci_connect_cfm(conn, ev->status);
hci_conn_drop(conn); hci_conn_drop(conn);
} }
@ -3679,7 +3679,7 @@ static void hci_remote_ext_features_evt(struct hci_dev *hdev,
if (!hci_outgoing_auth_needed(hdev, conn)) { if (!hci_outgoing_auth_needed(hdev, conn)) {
conn->state = BT_CONNECTED; conn->state = BT_CONNECTED;
hci_proto_connect_cfm(conn, ev->status); hci_connect_cfm(conn, ev->status);
hci_conn_drop(conn); hci_conn_drop(conn);
} }
@ -3738,7 +3738,7 @@ static void hci_sync_conn_complete_evt(struct hci_dev *hdev,
break; break;
} }
hci_proto_connect_cfm(conn, ev->status); hci_connect_cfm(conn, ev->status);
if (ev->status) if (ev->status)
hci_conn_del(conn); hci_conn_del(conn);
@ -3849,7 +3849,7 @@ static void hci_key_refresh_complete_evt(struct hci_dev *hdev,
if (!ev->status) if (!ev->status)
conn->state = BT_CONNECTED; conn->state = BT_CONNECTED;
hci_proto_connect_cfm(conn, ev->status); hci_connect_cfm(conn, ev->status);
hci_conn_drop(conn); hci_conn_drop(conn);
} else { } else {
hci_auth_cfm(conn, ev->status); hci_auth_cfm(conn, ev->status);
@ -4512,7 +4512,7 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_debugfs_create_conn(conn); hci_debugfs_create_conn(conn);
hci_conn_add_sysfs(conn); hci_conn_add_sysfs(conn);
hci_proto_connect_cfm(conn, ev->status); hci_connect_cfm(conn, ev->status);
params = hci_pend_le_action_lookup(&hdev->pend_le_conns, &conn->dst, params = hci_pend_le_action_lookup(&hdev->pend_le_conns, &conn->dst,
conn->dst_type); conn->dst_type);

View file

@ -46,9 +46,9 @@ struct hci_pinfo {
unsigned short channel; unsigned short channel;
}; };
static inline int hci_test_bit(int nr, void *addr) static inline int hci_test_bit(int nr, const void *addr)
{ {
return *((__u32 *) addr + (nr >> 5)) & ((__u32) 1 << (nr & 31)); return *((const __u32 *) addr + (nr >> 5)) & ((__u32) 1 << (nr & 31));
} }
/* Security filter */ /* Security filter */
@ -183,12 +183,13 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
kfree_skb(skb_copy); kfree_skb(skb_copy);
} }
/* Send frame to control socket */ /* Send frame to sockets with specific channel */
void hci_send_to_control(struct sk_buff *skb, struct sock *skip_sk) void hci_send_to_channel(unsigned short channel, struct sk_buff *skb,
struct sock *skip_sk)
{ {
struct sock *sk; struct sock *sk;
BT_DBG("len %d", skb->len); BT_DBG("channel %u len %d", channel, skb->len);
read_lock(&hci_sk_list.lock); read_lock(&hci_sk_list.lock);
@ -202,35 +203,7 @@ void hci_send_to_control(struct sk_buff *skb, struct sock *skip_sk)
if (sk->sk_state != BT_BOUND) if (sk->sk_state != BT_BOUND)
continue; continue;
if (hci_pi(sk)->channel != HCI_CHANNEL_CONTROL) if (hci_pi(sk)->channel != channel)
continue;
nskb = skb_clone(skb, GFP_ATOMIC);
if (!nskb)
continue;
if (sock_queue_rcv_skb(sk, nskb))
kfree_skb(nskb);
}
read_unlock(&hci_sk_list.lock);
}
static void queue_monitor_skb(struct sk_buff *skb)
{
struct sock *sk;
BT_DBG("len %d", skb->len);
read_lock(&hci_sk_list.lock);
sk_for_each(sk, &hci_sk_list.head) {
struct sk_buff *nskb;
if (sk->sk_state != BT_BOUND)
continue;
if (hci_pi(sk)->channel != HCI_CHANNEL_MONITOR)
continue; continue;
nskb = skb_clone(skb, GFP_ATOMIC); nskb = skb_clone(skb, GFP_ATOMIC);
@ -290,7 +263,7 @@ void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb)
hdr->index = cpu_to_le16(hdev->id); hdr->index = cpu_to_le16(hdev->id);
hdr->len = cpu_to_le16(skb->len); hdr->len = cpu_to_le16(skb->len);
queue_monitor_skb(skb_copy); hci_send_to_channel(HCI_CHANNEL_MONITOR, skb_copy, NULL);
kfree_skb(skb_copy); kfree_skb(skb_copy);
} }
@ -397,7 +370,7 @@ void hci_sock_dev_event(struct hci_dev *hdev, int event)
skb = create_monitor_event(hdev, event); skb = create_monitor_event(hdev, event);
if (skb) { if (skb) {
queue_monitor_skb(skb); hci_send_to_channel(HCI_CHANNEL_MONITOR, skb, NULL);
kfree_skb(skb); kfree_skb(skb);
} }
} }

View file

@ -1244,6 +1244,13 @@ static void l2cap_move_done(struct l2cap_chan *chan)
static void l2cap_chan_ready(struct l2cap_chan *chan) static void l2cap_chan_ready(struct l2cap_chan *chan)
{ {
/* The channel may have already been flagged as connected in
* case of receiving data before the L2CAP info req/rsp
* procedure is complete.
*/
if (chan->state == BT_CONNECTED)
return;
/* This clears all conf flags, including CONF_NOT_COMPLETE */ /* This clears all conf flags, including CONF_NOT_COMPLETE */
chan->conf_state = 0; chan->conf_state = 0;
__clear_chan_timer(chan); __clear_chan_timer(chan);
@ -6785,6 +6792,13 @@ static void l2cap_data_channel(struct l2cap_conn *conn, u16 cid,
BT_DBG("chan %p, len %d", chan, skb->len); BT_DBG("chan %p, len %d", chan, skb->len);
/* If we receive data on a fixed channel before the info req/rsp
* procdure is done simply assume that the channel is supported
* and mark it as ready.
*/
if (chan->chan_type == L2CAP_CHAN_FIXED)
l2cap_chan_ready(chan);
if (chan->state != BT_CONNECTED) if (chan->state != BT_CONNECTED)
goto drop; goto drop;
@ -7238,13 +7252,16 @@ static struct l2cap_chan *l2cap_global_fixed_chan(struct l2cap_chan *c,
return NULL; return NULL;
} }
void l2cap_connect_cfm(struct hci_conn *hcon, u8 status) static void l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
{ {
struct hci_dev *hdev = hcon->hdev; struct hci_dev *hdev = hcon->hdev;
struct l2cap_conn *conn; struct l2cap_conn *conn;
struct l2cap_chan *pchan; struct l2cap_chan *pchan;
u8 dst_type; u8 dst_type;
if (hcon->type != ACL_LINK && hcon->type != LE_LINK)
return;
BT_DBG("hcon %p bdaddr %pMR status %d", hcon, &hcon->dst, status); BT_DBG("hcon %p bdaddr %pMR status %d", hcon, &hcon->dst, status);
if (status) { if (status) {
@ -7307,8 +7324,11 @@ int l2cap_disconn_ind(struct hci_conn *hcon)
return conn->disc_reason; return conn->disc_reason;
} }
void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason) static void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason)
{ {
if (hcon->type != ACL_LINK && hcon->type != LE_LINK)
return;
BT_DBG("hcon %p reason %d", hcon, reason); BT_DBG("hcon %p reason %d", hcon, reason);
l2cap_conn_del(hcon, bt_to_errno(reason)); l2cap_conn_del(hcon, bt_to_errno(reason));
@ -7331,13 +7351,13 @@ static inline void l2cap_check_encryption(struct l2cap_chan *chan, u8 encrypt)
} }
} }
int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) static void l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
{ {
struct l2cap_conn *conn = hcon->l2cap_data; struct l2cap_conn *conn = hcon->l2cap_data;
struct l2cap_chan *chan; struct l2cap_chan *chan;
if (!conn) if (!conn)
return 0; return;
BT_DBG("conn %p status 0x%2.2x encrypt %u", conn, status, encrypt); BT_DBG("conn %p status 0x%2.2x encrypt %u", conn, status, encrypt);
@ -7420,8 +7440,6 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
} }
mutex_unlock(&conn->chan_lock); mutex_unlock(&conn->chan_lock);
return 0;
} }
int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags) int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
@ -7529,6 +7547,13 @@ drop:
return 0; return 0;
} }
static struct hci_cb l2cap_cb = {
.name = "L2CAP",
.connect_cfm = l2cap_connect_cfm,
.disconn_cfm = l2cap_disconn_cfm,
.security_cfm = l2cap_security_cfm,
};
static int l2cap_debugfs_show(struct seq_file *f, void *p) static int l2cap_debugfs_show(struct seq_file *f, void *p)
{ {
struct l2cap_chan *c; struct l2cap_chan *c;
@ -7570,6 +7595,8 @@ int __init l2cap_init(void)
if (err < 0) if (err < 0)
return err; return err;
hci_register_cb(&l2cap_cb);
if (IS_ERR_OR_NULL(bt_debugfs)) if (IS_ERR_OR_NULL(bt_debugfs))
return 0; return 0;
@ -7587,6 +7614,7 @@ int __init l2cap_init(void)
void l2cap_exit(void) void l2cap_exit(void)
{ {
debugfs_remove(l2cap_debugfs); debugfs_remove(l2cap_debugfs);
hci_unregister_cb(&l2cap_cb);
l2cap_cleanup_sockets(); l2cap_cleanup_sockets();
} }

View file

@ -29,6 +29,7 @@
#include <net/bluetooth/bluetooth.h> #include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h> #include <net/bluetooth/hci_core.h>
#include <net/bluetooth/hci_sock.h>
#include <net/bluetooth/l2cap.h> #include <net/bluetooth/l2cap.h>
#include <net/bluetooth/mgmt.h> #include <net/bluetooth/mgmt.h>
@ -242,7 +243,7 @@ static int mgmt_event(u16 event, struct hci_dev *hdev, void *data, u16 data_len,
/* Time stamp */ /* Time stamp */
__net_timestamp(skb); __net_timestamp(skb);
hci_send_to_control(skb, skip_sk); hci_send_to_channel(HCI_CHANNEL_CONTROL, skb, skip_sk);
kfree_skb(skb); kfree_skb(skb);
return 0; return 0;
@ -2116,8 +2117,7 @@ static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
goto failed; goto failed;
} }
if (mgmt_pending_find(MGMT_OP_SET_SSP, hdev) || if (mgmt_pending_find(MGMT_OP_SET_SSP, hdev)) {
mgmt_pending_find(MGMT_OP_SET_HS, hdev)) {
err = cmd_status(sk, hdev->id, MGMT_OP_SET_SSP, err = cmd_status(sk, hdev->id, MGMT_OP_SET_SSP,
MGMT_STATUS_BUSY); MGMT_STATUS_BUSY);
goto failed; goto failed;
@ -2176,6 +2176,12 @@ static int set_hs(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
hci_dev_lock(hdev); hci_dev_lock(hdev);
if (mgmt_pending_find(MGMT_OP_SET_SSP, hdev)) {
err = cmd_status(sk, hdev->id, MGMT_OP_SET_HS,
MGMT_STATUS_BUSY);
goto unlock;
}
if (cp->val) { if (cp->val) {
changed = !test_and_set_bit(HCI_HS_ENABLED, &hdev->dev_flags); changed = !test_and_set_bit(HCI_HS_ENABLED, &hdev->dev_flags);
} else { } else {
@ -3249,6 +3255,10 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
if (PTR_ERR(conn) == -EBUSY) if (PTR_ERR(conn) == -EBUSY)
status = MGMT_STATUS_BUSY; status = MGMT_STATUS_BUSY;
else if (PTR_ERR(conn) == -EOPNOTSUPP)
status = MGMT_STATUS_NOT_SUPPORTED;
else if (PTR_ERR(conn) == -ECONNREFUSED)
status = MGMT_STATUS_REJECTED;
else else
status = MGMT_STATUS_CONNECT_FAILED; status = MGMT_STATUS_CONNECT_FAILED;
@ -6654,7 +6664,7 @@ void mgmt_new_csrk(struct hci_dev *hdev, struct smp_csrk *csrk,
bacpy(&ev.key.addr.bdaddr, &csrk->bdaddr); bacpy(&ev.key.addr.bdaddr, &csrk->bdaddr);
ev.key.addr.type = link_to_bdaddr(LE_LINK, csrk->bdaddr_type); ev.key.addr.type = link_to_bdaddr(LE_LINK, csrk->bdaddr_type);
ev.key.master = csrk->master; ev.key.type = csrk->type;
memcpy(ev.key.val, csrk->val, sizeof(csrk->val)); memcpy(ev.key.val, csrk->val, sizeof(csrk->val));
mgmt_event(MGMT_EV_NEW_CSRK, hdev, &ev, sizeof(ev), NULL); mgmt_event(MGMT_EV_NEW_CSRK, hdev, &ev, sizeof(ev), NULL);

View file

@ -1083,9 +1083,13 @@ int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
return lm; return lm;
} }
void sco_connect_cfm(struct hci_conn *hcon, __u8 status) static void sco_connect_cfm(struct hci_conn *hcon, __u8 status)
{ {
if (hcon->type != SCO_LINK && hcon->type != ESCO_LINK)
return;
BT_DBG("hcon %p bdaddr %pMR status %d", hcon, &hcon->dst, status); BT_DBG("hcon %p bdaddr %pMR status %d", hcon, &hcon->dst, status);
if (!status) { if (!status) {
struct sco_conn *conn; struct sco_conn *conn;
@ -1096,8 +1100,11 @@ void sco_connect_cfm(struct hci_conn *hcon, __u8 status)
sco_conn_del(hcon, bt_to_errno(status)); sco_conn_del(hcon, bt_to_errno(status));
} }
void sco_disconn_cfm(struct hci_conn *hcon, __u8 reason) static void sco_disconn_cfm(struct hci_conn *hcon, __u8 reason)
{ {
if (hcon->type != SCO_LINK && hcon->type != ESCO_LINK)
return;
BT_DBG("hcon %p reason %d", hcon, reason); BT_DBG("hcon %p reason %d", hcon, reason);
sco_conn_del(hcon, bt_to_errno(reason)); sco_conn_del(hcon, bt_to_errno(reason));
@ -1122,6 +1129,12 @@ drop:
return 0; return 0;
} }
static struct hci_cb sco_cb = {
.name = "SCO",
.connect_cfm = sco_connect_cfm,
.disconn_cfm = sco_disconn_cfm,
};
static int sco_debugfs_show(struct seq_file *f, void *p) static int sco_debugfs_show(struct seq_file *f, void *p)
{ {
struct sock *sk; struct sock *sk;
@ -1203,6 +1216,8 @@ int __init sco_init(void)
BT_INFO("SCO socket layer initialized"); BT_INFO("SCO socket layer initialized");
hci_register_cb(&sco_cb);
if (IS_ERR_OR_NULL(bt_debugfs)) if (IS_ERR_OR_NULL(bt_debugfs))
return 0; return 0;
@ -1222,6 +1237,8 @@ void __exit sco_exit(void)
debugfs_remove(sco_debugfs); debugfs_remove(sco_debugfs);
hci_unregister_cb(&sco_cb);
bt_sock_unregister(BTPROTO_SCO); bt_sock_unregister(BTPROTO_SCO);
proto_unregister(&sco_proto); proto_unregister(&sco_proto);

View file

@ -1252,7 +1252,10 @@ static void smp_distribute_keys(struct smp_chan *smp)
csrk = kzalloc(sizeof(*csrk), GFP_KERNEL); csrk = kzalloc(sizeof(*csrk), GFP_KERNEL);
if (csrk) { if (csrk) {
csrk->master = 0x00; if (hcon->sec_level > BT_SECURITY_MEDIUM)
csrk->type = MGMT_CSRK_LOCAL_AUTHENTICATED;
else
csrk->type = MGMT_CSRK_LOCAL_UNAUTHENTICATED;
memcpy(csrk->val, sign.csrk, sizeof(csrk->val)); memcpy(csrk->val, sign.csrk, sizeof(csrk->val));
} }
smp->slave_csrk = csrk; smp->slave_csrk = csrk;
@ -2352,7 +2355,10 @@ static int smp_cmd_sign_info(struct l2cap_conn *conn, struct sk_buff *skb)
csrk = kzalloc(sizeof(*csrk), GFP_KERNEL); csrk = kzalloc(sizeof(*csrk), GFP_KERNEL);
if (csrk) { if (csrk) {
csrk->master = 0x01; if (conn->hcon->sec_level > BT_SECURITY_MEDIUM)
csrk->type = MGMT_CSRK_REMOTE_AUTHENTICATED;
else
csrk->type = MGMT_CSRK_REMOTE_UNAUTHENTICATED;
memcpy(csrk->val, rp->csrk, sizeof(csrk->val)); memcpy(csrk->val, rp->csrk, sizeof(csrk->val));
} }
smp->csrk = csrk; smp->csrk = csrk;
@ -2951,24 +2957,14 @@ create_chan:
l2cap_chan_set_defaults(chan); l2cap_chan_set_defaults(chan);
if (cid == L2CAP_CID_SMP) { if (cid == L2CAP_CID_SMP) {
/* If usage of static address is forced or if the devices u8 bdaddr_type;
* does not have a public address, then listen on the static
* address. hci_copy_identity_address(hdev, &chan->src, &bdaddr_type);
*
* In case BR/EDR has been disabled on a dual-mode controller if (bdaddr_type == ADDR_LE_DEV_PUBLIC)
* and a static address has been configued, then listen on
* the static address instead.
*/
if (test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags) ||
!bacmp(&hdev->bdaddr, BDADDR_ANY) ||
(!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags) &&
bacmp(&hdev->static_addr, BDADDR_ANY))) {
bacpy(&chan->src, &hdev->static_addr);
chan->src_type = BDADDR_LE_RANDOM;
} else {
bacpy(&chan->src, &hdev->bdaddr);
chan->src_type = BDADDR_LE_PUBLIC; chan->src_type = BDADDR_LE_PUBLIC;
} else
chan->src_type = BDADDR_LE_RANDOM;
} else { } else {
bacpy(&chan->src, &hdev->bdaddr); bacpy(&chan->src, &hdev->bdaddr);
chan->src_type = BDADDR_BREDR; chan->src_type = BDADDR_BREDR;

View file

@ -126,6 +126,7 @@ static void lowpan_setup(struct net_device *dev)
dev->header_ops = &lowpan_header_ops; dev->header_ops = &lowpan_header_ops;
dev->ml_priv = &lowpan_mlme; dev->ml_priv = &lowpan_mlme;
dev->destructor = free_netdev; dev->destructor = free_netdev;
dev->features |= NETIF_F_NETNS_LOCAL;
} }
static int lowpan_validate(struct nlattr *tb[], struct nlattr *data[]) static int lowpan_validate(struct nlattr *tb[], struct nlattr *data[])
@ -148,10 +149,11 @@ static int lowpan_newlink(struct net *src_net, struct net_device *dev,
pr_debug("adding new link\n"); pr_debug("adding new link\n");
if (!tb[IFLA_LINK]) if (!tb[IFLA_LINK] ||
!net_eq(dev_net(dev), &init_net))
return -EINVAL; return -EINVAL;
/* find and hold real wpan device */ /* find and hold real wpan device */
real_dev = dev_get_by_index(src_net, nla_get_u32(tb[IFLA_LINK])); real_dev = dev_get_by_index(dev_net(dev), nla_get_u32(tb[IFLA_LINK]));
if (!real_dev) if (!real_dev)
return -ENODEV; return -ENODEV;
if (real_dev->type != ARPHRD_IEEE802154) { if (real_dev->type != ARPHRD_IEEE802154) {

View file

@ -225,6 +225,7 @@ static int cfg802154_netdev_notifier_call(struct notifier_block *nb,
switch (state) { switch (state) {
/* TODO NETDEV_DEVTYPE */ /* TODO NETDEV_DEVTYPE */
case NETDEV_REGISTER: case NETDEV_REGISTER:
dev->features |= NETIF_F_NETNS_LOCAL;
wpan_dev->identifier = ++rdev->wpan_dev_id; wpan_dev->identifier = ++rdev->wpan_dev_id;
list_add_rcu(&wpan_dev->list, &rdev->wpan_dev_list); list_add_rcu(&wpan_dev->list, &rdev->wpan_dev_list);
rdev->devlist_generation++; rdev->devlist_generation++;