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:
Коммит
70c836a4d1
|
@ -6,11 +6,14 @@ Required properties:
|
|||
- spi-max-frequency: maximal bus speed, should be set to 7500000 depends
|
||||
sync or async operation mode
|
||||
- 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:
|
||||
- reset-gpio: GPIO spec for the rstn 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:
|
||||
|
||||
|
@ -18,6 +21,7 @@ Example:
|
|||
compatible = "atmel,at86rf231";
|
||||
spi-max-frequency = <7500000>;
|
||||
reg = <0>;
|
||||
interrupts = <19 1>;
|
||||
interrupts = <19 4>;
|
||||
interrupt-parent = <&gpio3>;
|
||||
xtal-trim = /bits/ 8 <0x06>;
|
||||
};
|
||||
|
|
|
@ -65,6 +65,7 @@ static const struct usb_device_id ath3k_table[] = {
|
|||
/* Atheros AR3011 with sflash firmware*/
|
||||
{ USB_DEVICE(0x0489, 0xE027) },
|
||||
{ USB_DEVICE(0x0489, 0xE03D) },
|
||||
{ USB_DEVICE(0x04F2, 0xAFF1) },
|
||||
{ USB_DEVICE(0x0930, 0x0215) },
|
||||
{ USB_DEVICE(0x0CF3, 0x3002) },
|
||||
{ USB_DEVICE(0x0CF3, 0xE019) },
|
||||
|
|
|
@ -111,6 +111,7 @@ struct btmrvl_private {
|
|||
|
||||
/* Vendor specific Bluetooth commands */
|
||||
#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_AUTO_SLEEP_MODE 0xFC23
|
||||
#define BT_CMD_HOST_SLEEP_CONFIG 0xFC59
|
||||
|
|
|
@ -230,6 +230,18 @@ int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, u8 subcmd)
|
|||
}
|
||||
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)
|
||||
{
|
||||
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_enable_sco_routing_to_host(priv);
|
||||
|
||||
btmrvl_pscan_window_reporting(priv, 0x01);
|
||||
|
||||
priv->btmrvl_dev.psmode = 1;
|
||||
|
|
|
@ -159,6 +159,7 @@ static const struct usb_device_id blacklist_table[] = {
|
|||
/* Atheros 3011 with sflash firmware */
|
||||
{ USB_DEVICE(0x0489, 0xe027), .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(0x0cf3, 0x3002), .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);
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
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
|
||||
* of this device.
|
||||
*/
|
||||
err = btusb_wait_on_bit_timeout(&data->flags, BTUSB_DOWNLOADING,
|
||||
msecs_to_jiffies(5000),
|
||||
TASK_INTERRUPTIBLE);
|
||||
err = wait_on_bit_timeout(&data->flags, BTUSB_DOWNLOADING,
|
||||
TASK_INTERRUPTIBLE,
|
||||
msecs_to_jiffies(5000));
|
||||
if (err == 1) {
|
||||
BT_ERR("%s: Firmware loading interrupted", hdev->name);
|
||||
err = -EINTR;
|
||||
|
@ -2249,9 +2240,9 @@ done:
|
|||
*/
|
||||
BT_INFO("%s: Waiting for device to boot", hdev->name);
|
||||
|
||||
err = btusb_wait_on_bit_timeout(&data->flags, BTUSB_BOOTING,
|
||||
msecs_to_jiffies(1000),
|
||||
TASK_INTERRUPTIBLE);
|
||||
err = wait_on_bit_timeout(&data->flags, BTUSB_BOOTING,
|
||||
TASK_INTERRUPTIBLE,
|
||||
msecs_to_jiffies(1000));
|
||||
|
||||
if (err == 1) {
|
||||
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;
|
||||
}
|
||||
|
||||
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,
|
||||
const bdaddr_t *bdaddr)
|
||||
{
|
||||
|
@ -2354,6 +2366,23 @@ static int btusb_set_bdaddr_marvell(struct hci_dev *hdev,
|
|||
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}})
|
||||
|
||||
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;
|
||||
const struct hci_command_hdr *cmd;
|
||||
const u8 *cmd_param;
|
||||
u16 opcode;
|
||||
u16 opcode, subver, rev;
|
||||
const char *hw_name = NULL;
|
||||
struct sk_buff *skb;
|
||||
struct hci_rp_read_local_version *ver;
|
||||
struct hci_rp_read_bd_addr *bda;
|
||||
long ret;
|
||||
|
||||
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;
|
||||
}
|
||||
int i;
|
||||
|
||||
/* Reset */
|
||||
skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
ret = PTR_ERR(skb);
|
||||
BT_ERR("%s: HCI_OP_RESET failed (%ld)", hdev->name, ret);
|
||||
goto done;
|
||||
return ret;
|
||||
}
|
||||
kfree_skb(skb);
|
||||
|
||||
|
@ -2399,23 +2419,43 @@ static int btusb_setup_bcm_patchram(struct hci_dev *hdev)
|
|||
ret = PTR_ERR(skb);
|
||||
BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION failed (%ld)",
|
||||
hdev->name, ret);
|
||||
goto done;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (skb->len != sizeof(*ver)) {
|
||||
BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION event length mismatch",
|
||||
hdev->name);
|
||||
kfree_skb(skb);
|
||||
ret = -EIO;
|
||||
goto done;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
ver = (struct hci_rp_read_local_version *)skb->data;
|
||||
BT_INFO("%s: BCM: patching hci_ver=%02x hci_rev=%04x lmp_ver=%02x "
|
||||
"lmp_subver=%04x", hdev->name, ver->hci_ver, ver->hci_rev,
|
||||
ver->lmp_ver, ver->lmp_subver);
|
||||
rev = le16_to_cpu(ver->hci_rev);
|
||||
subver = le16_to_cpu(ver->lmp_subver);
|
||||
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 */
|
||||
skb = __hci_cmd_sync(hdev, 0xfc2e, 0, NULL, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
|
@ -2493,11 +2533,14 @@ reset_fw:
|
|||
}
|
||||
|
||||
ver = (struct hci_rp_read_local_version *)skb->data;
|
||||
BT_INFO("%s: BCM: firmware hci_ver=%02x hci_rev=%04x lmp_ver=%02x "
|
||||
"lmp_subver=%04x", hdev->name, ver->hci_ver, ver->hci_rev,
|
||||
ver->lmp_ver, ver->lmp_subver);
|
||||
rev = le16_to_cpu(ver->hci_rev);
|
||||
subver = le16_to_cpu(ver->lmp_subver);
|
||||
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 */
|
||||
skb = __hci_cmd_sync(hdev, HCI_OP_READ_BD_ADDR, 0, NULL,
|
||||
HCI_INIT_TIMEOUT);
|
||||
|
@ -2708,6 +2751,7 @@ static int btusb_probe(struct usb_interface *intf,
|
|||
|
||||
if (id->driver_info & BTUSB_INTEL) {
|
||||
hdev->setup = btusb_setup_intel;
|
||||
hdev->shutdown = btusb_shutdown_intel;
|
||||
hdev->set_bdaddr = btusb_set_bdaddr_intel;
|
||||
set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
|
||||
}
|
||||
|
|
|
@ -46,8 +46,6 @@ struct at86rf2xx_chip_data {
|
|||
u16 t_off_to_tx_on;
|
||||
u16 t_frame;
|
||||
u16 t_p_ack;
|
||||
/* completion timeout for tx in msecs */
|
||||
u16 t_tx_timeout;
|
||||
int rssi_base_val;
|
||||
|
||||
int (*set_channel)(struct at86rf230_local *, u8, u8);
|
||||
|
@ -689,7 +687,7 @@ at86rf230_sync_state_change_complete(void *context)
|
|||
static int
|
||||
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_sync_state_change_complete,
|
||||
|
@ -1281,7 +1279,6 @@ static struct at86rf2xx_chip_data at86rf233_data = {
|
|||
.t_off_to_tx_on = 80,
|
||||
.t_frame = 4096,
|
||||
.t_p_ack = 545,
|
||||
.t_tx_timeout = 2000,
|
||||
.rssi_base_val = -91,
|
||||
.set_channel = at86rf23x_set_channel,
|
||||
.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_frame = 4096,
|
||||
.t_p_ack = 545,
|
||||
.t_tx_timeout = 2000,
|
||||
.rssi_base_val = -91,
|
||||
.set_channel = at86rf23x_set_channel,
|
||||
.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_frame = 4096,
|
||||
.t_p_ack = 545,
|
||||
.t_tx_timeout = 2000,
|
||||
.rssi_base_val = -100,
|
||||
.set_channel = at86rf212_set_channel,
|
||||
.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;
|
||||
unsigned int dvdd;
|
||||
|
@ -1326,7 +1321,12 @@ static int at86rf230_hw_init(struct at86rf230_local *lp)
|
|||
return rc;
|
||||
|
||||
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;
|
||||
|
||||
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)
|
||||
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));
|
||||
rc = at86rf230_write_subreg(lp, SR_CSMA_SEED_0, csma_seed[0]);
|
||||
if (rc)
|
||||
|
@ -1362,6 +1367,45 @@ static int at86rf230_hw_init(struct at86rf230_local *lp)
|
|||
usleep_range(lp->data->t_sleep_cycle,
|
||||
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);
|
||||
if (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);
|
||||
}
|
||||
|
||||
static struct at86rf230_platform_data *
|
||||
at86rf230_get_pdata(struct spi_device *spi)
|
||||
static int
|
||||
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)
|
||||
return spi->dev.platform_data;
|
||||
if (!IS_ENABLED(CONFIG_OF) || !spi->dev.of_node) {
|
||||
if (!pdata)
|
||||
return -ENOENT;
|
||||
|
||||
pdata = devm_kzalloc(&spi->dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
goto done;
|
||||
*rstn = pdata->rstn;
|
||||
*slp_tr = pdata->slp_tr;
|
||||
*xtal_trim = pdata->xtal_trim;
|
||||
return 0;
|
||||
}
|
||||
|
||||
pdata->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);
|
||||
*rstn = of_get_named_gpio(spi->dev.of_node, "reset-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;
|
||||
done:
|
||||
return pdata;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -1501,43 +1551,43 @@ at86rf230_setup_spi_messages(struct at86rf230_local *lp)
|
|||
|
||||
static int at86rf230_probe(struct spi_device *spi)
|
||||
{
|
||||
struct at86rf230_platform_data *pdata;
|
||||
struct ieee802154_hw *hw;
|
||||
struct at86rf230_local *lp;
|
||||
unsigned int status;
|
||||
int rc, irq_type;
|
||||
int rc, irq_type, rstn, slp_tr;
|
||||
u8 xtal_trim;
|
||||
|
||||
if (!spi->irq) {
|
||||
dev_err(&spi->dev, "no IRQ specified\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pdata = at86rf230_get_pdata(spi);
|
||||
if (!pdata) {
|
||||
dev_err(&spi->dev, "no platform_data\n");
|
||||
return -EINVAL;
|
||||
rc = at86rf230_get_pdata(spi, &rstn, &slp_tr, &xtal_trim);
|
||||
if (rc < 0) {
|
||||
dev_err(&spi->dev, "failed to parse platform_data: %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (gpio_is_valid(pdata->rstn)) {
|
||||
rc = devm_gpio_request_one(&spi->dev, pdata->rstn,
|
||||
if (gpio_is_valid(rstn)) {
|
||||
rc = devm_gpio_request_one(&spi->dev, rstn,
|
||||
GPIOF_OUT_INIT_HIGH, "rstn");
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (gpio_is_valid(pdata->slp_tr)) {
|
||||
rc = devm_gpio_request_one(&spi->dev, pdata->slp_tr,
|
||||
if (gpio_is_valid(slp_tr)) {
|
||||
rc = devm_gpio_request_one(&spi->dev, slp_tr,
|
||||
GPIOF_OUT_INIT_LOW, "slp_tr");
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Reset */
|
||||
if (gpio_is_valid(pdata->rstn)) {
|
||||
if (gpio_is_valid(rstn)) {
|
||||
udelay(1);
|
||||
gpio_set_value(pdata->rstn, 0);
|
||||
gpio_set_value(rstn, 0);
|
||||
udelay(1);
|
||||
gpio_set_value(pdata->rstn, 1);
|
||||
gpio_set_value(rstn, 1);
|
||||
usleep_range(120, 240);
|
||||
}
|
||||
|
||||
|
@ -1571,7 +1621,7 @@ static int at86rf230_probe(struct spi_device *spi)
|
|||
|
||||
spi_set_drvdata(spi, lp);
|
||||
|
||||
rc = at86rf230_hw_init(lp);
|
||||
rc = at86rf230_hw_init(lp, xtal_trim);
|
||||
if (rc)
|
||||
goto free_dev;
|
||||
|
||||
|
|
|
@ -28,7 +28,8 @@
|
|||
#include <asm/byteorder.h>
|
||||
|
||||
#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_ADDR_SHORT_BROADCAST 0xffff
|
||||
|
@ -204,11 +205,18 @@ enum {
|
|||
|
||||
/**
|
||||
* 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)
|
||||
*/
|
||||
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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -22,6 +22,7 @@ struct at86rf230_platform_data {
|
|||
int rstn;
|
||||
int slp_tr;
|
||||
int dig2;
|
||||
u8 xtal_trim;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -108,7 +108,7 @@ struct bt_uuid {
|
|||
struct smp_csrk {
|
||||
bdaddr_t bdaddr;
|
||||
u8 bdaddr_type;
|
||||
u8 master;
|
||||
u8 type;
|
||||
u8 val[16];
|
||||
};
|
||||
|
||||
|
@ -373,6 +373,7 @@ struct hci_dev {
|
|||
int (*close)(struct hci_dev *hdev);
|
||||
int (*flush)(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);
|
||||
void (*notify)(struct hci_dev *hdev, unsigned int evt);
|
||||
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_cb_list;
|
||||
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 ----- */
|
||||
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);
|
||||
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 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);
|
||||
|
||||
/* ----- 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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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 ----- */
|
||||
struct hci_cb {
|
||||
struct list_head list;
|
||||
|
||||
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,
|
||||
__u8 encrypt);
|
||||
void (*key_change_cfm) (struct hci_conn *conn, __u8 status);
|
||||
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)
|
||||
{
|
||||
struct hci_cb *cb;
|
||||
__u8 encrypt;
|
||||
|
||||
hci_proto_auth_cfm(conn, status);
|
||||
|
||||
if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags))
|
||||
return;
|
||||
|
||||
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) {
|
||||
if (cb->security_cfm)
|
||||
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,
|
||||
|
@ -1178,26 +1130,27 @@ static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status,
|
|||
if (conn->pending_sec_level > conn->sec_level)
|
||||
conn->sec_level = conn->pending_sec_level;
|
||||
|
||||
hci_proto_encrypt_cfm(conn, status, encrypt);
|
||||
|
||||
read_lock(&hci_cb_list_lock);
|
||||
mutex_lock(&hci_cb_list_lock);
|
||||
list_for_each_entry(cb, &hci_cb_list, list) {
|
||||
if (cb->security_cfm)
|
||||
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)
|
||||
{
|
||||
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) {
|
||||
if (cb->key_change_cfm)
|
||||
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,
|
||||
|
@ -1205,12 +1158,12 @@ static inline void hci_role_switch_cfm(struct hci_conn *conn, __u8 status,
|
|||
{
|
||||
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) {
|
||||
if (cb->role_switch_cfm)
|
||||
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)
|
||||
|
@ -1312,7 +1265,8 @@ void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode);
|
|||
|
||||
/* ----- HCI Sockets ----- */
|
||||
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_sock_dev_event(struct hci_dev *hdev, int event);
|
||||
|
|
|
@ -647,9 +647,14 @@ struct mgmt_ev_new_irk {
|
|||
struct mgmt_irk_info irk;
|
||||
} __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_addr_info addr;
|
||||
__u8 master;
|
||||
__u8 type;
|
||||
__u8 val[16];
|
||||
} __packed;
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <net/af_ieee802154.h>
|
||||
#include <linux/ieee802154.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/unaligned/memmove.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)
|
||||
{
|
||||
__le64 tmp = (__force __le64)swab64p(be64_src);
|
||||
|
||||
memcpy(le64_dst, &tmp, IEEE802154_EXTENDED_ADDR_LEN);
|
||||
__put_unaligned_memmove64(swab64p(be64_src), le64_dst);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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)
|
||||
{
|
||||
__be64 tmp = (__force __be64)swab64p(le64_src);
|
||||
|
||||
memcpy(be64_dst, &tmp, IEEE802154_EXTENDED_ADDR_LEN);
|
||||
__put_unaligned_memmove64(swab64p(le64_src), be64_dst);
|
||||
}
|
||||
|
||||
/* Basic interface to register ieee802154 hwice */
|
||||
|
|
|
@ -1,6 +1,61 @@
|
|||
config 6LOWPAN
|
||||
menuconfig 6LOWPAN
|
||||
tristate "6LoWPAN Support"
|
||||
depends on IPV6
|
||||
---help---
|
||||
This enables IPv6 over Low power Wireless Personal Area Network -
|
||||
"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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -54,6 +54,8 @@
|
|||
#include <net/ipv6.h>
|
||||
#include <net/af_ieee802154.h>
|
||||
|
||||
#include "nhc.h"
|
||||
|
||||
/* Uncompress address function for source and
|
||||
* destination address(non-multicast).
|
||||
*
|
||||
|
@ -224,77 +226,6 @@ static int lowpan_uncompress_multicast_daddr(struct sk_buff *skb,
|
|||
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 */
|
||||
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;
|
||||
}
|
||||
|
||||
/* UDP data uncompression */
|
||||
/* Next header data uncompression */
|
||||
if (iphc0 & LOWPAN_IPHC_NH_C) {
|
||||
struct udphdr uh;
|
||||
const int needed = sizeof(struct udphdr) + sizeof(hdr);
|
||||
|
||||
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))
|
||||
err = lowpan_nhc_do_uncompression(skb, dev, &hdr);
|
||||
if (err < 0)
|
||||
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 {
|
||||
err = skb_cow(skb, sizeof(hdr));
|
||||
if (unlikely(err))
|
||||
|
@ -500,71 +413,6 @@ static u8 lowpan_compress_addr_64(u8 **hc_ptr, u8 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,
|
||||
unsigned short type, const void *_daddr,
|
||||
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;
|
||||
struct ipv6hdr *hdr;
|
||||
u8 head[100] = {};
|
||||
int addr_type;
|
||||
int ret, addr_type;
|
||||
|
||||
if (type != ETH_P_IPV6)
|
||||
return -EINVAL;
|
||||
|
@ -649,13 +497,12 @@ int lowpan_header_compress(struct sk_buff *skb, struct net_device *dev,
|
|||
|
||||
/* NOTE: payload length is always compressed */
|
||||
|
||||
/* Next Header is compress if UDP */
|
||||
if (hdr->nexthdr == UIP_PROTO_UDP)
|
||||
iphc0 |= LOWPAN_IPHC_NH_C;
|
||||
|
||||
if ((iphc0 & LOWPAN_IPHC_NH_C) == 0)
|
||||
lowpan_push_hc_data(&hc_ptr, &hdr->nexthdr,
|
||||
sizeof(hdr->nexthdr));
|
||||
/* Check if we provide the nhc format for nexthdr and compression
|
||||
* functionality. If not nexthdr is handled inline and not compressed.
|
||||
*/
|
||||
ret = lowpan_nhc_check_compression(skb, hdr, &hc_ptr, &iphc0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Hop limit
|
||||
* 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 */
|
||||
if (hdr->nexthdr == UIP_PROTO_UDP)
|
||||
compress_udp_header(&hc_ptr, skb);
|
||||
/* next header compression */
|
||||
if (iphc0 & LOWPAN_IPHC_NH_C) {
|
||||
ret = lowpan_nhc_do_compression(skb, hdr, &hc_ptr);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
head[0] = iphc0;
|
||||
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);
|
||||
|
||||
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");
|
||||
|
|
|
@ -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);
|
|
@ -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 */
|
|
@ -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");
|
|
@ -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");
|
|
@ -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");
|
|
@ -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");
|
|
@ -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");
|
|
@ -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");
|
|
@ -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");
|
|
@ -91,4 +91,12 @@ config BT_SELFTEST_SMP
|
|||
Run test cases for SMP cryptographic functionality, including both
|
||||
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"
|
||||
|
|
|
@ -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 \
|
||||
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
|
||||
|
||||
subdir-ccflags-y += -D__CHECK_ENDIAN__
|
||||
|
|
|
@ -19,9 +19,11 @@
|
|||
#include "a2mp.h"
|
||||
#include "amp.h"
|
||||
|
||||
#define A2MP_FEAT_EXT 0x8000
|
||||
|
||||
/* Global AMP Manager list */
|
||||
LIST_HEAD(amp_mgr_list);
|
||||
DEFINE_MUTEX(amp_mgr_list_lock);
|
||||
static LIST_HEAD(amp_mgr_list);
|
||||
static DEFINE_MUTEX(amp_mgr_list_lock);
|
||||
|
||||
/* A2MP build & send command helper functions */
|
||||
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;
|
||||
}
|
||||
|
||||
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 a2mp_cmd *cmd;
|
||||
|
@ -67,7 +69,7 @@ void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, void *data)
|
|||
kfree(cmd);
|
||||
}
|
||||
|
||||
u8 __next_ident(struct amp_mgr *mgr)
|
||||
static u8 __next_ident(struct amp_mgr *mgr)
|
||||
{
|
||||
if (++mgr->ident == 0)
|
||||
mgr->ident = 1;
|
||||
|
@ -75,6 +77,23 @@ u8 __next_ident(struct amp_mgr *mgr)
|
|||
return mgr->ident;
|
||||
}
|
||||
|
||||
static struct amp_mgr *amp_mgr_lookup_by_state(u8 state)
|
||||
{
|
||||
struct amp_mgr *mgr;
|
||||
|
||||
mutex_lock(&_mgr_list_lock);
|
||||
list_for_each_entry(mgr, &_mgr_list, list) {
|
||||
if (test_and_clear_bit(state, &mgr->state)) {
|
||||
amp_mgr_get(mgr);
|
||||
mutex_unlock(&_mgr_list_lock);
|
||||
return mgr;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&_mgr_list_lock);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* hci_dev_list shall be locked */
|
||||
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;
|
||||
}
|
||||
|
||||
struct amp_mgr *amp_mgr_lookup_by_state(u8 state)
|
||||
{
|
||||
struct amp_mgr *mgr;
|
||||
|
||||
mutex_lock(&_mgr_list_lock);
|
||||
list_for_each_entry(mgr, &_mgr_list, list) {
|
||||
if (test_and_clear_bit(state, &mgr->state)) {
|
||||
amp_mgr_get(mgr);
|
||||
mutex_unlock(&_mgr_list_lock);
|
||||
return mgr;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&_mgr_list_lock);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void a2mp_send_getinfo_rsp(struct hci_dev *hdev)
|
||||
{
|
||||
struct amp_mgr *mgr;
|
||||
|
|
|
@ -17,8 +17,6 @@
|
|||
|
||||
#include <net/bluetooth/l2cap.h>
|
||||
|
||||
#define A2MP_FEAT_EXT 0x8000
|
||||
|
||||
enum amp_mgr_state {
|
||||
READ_LOC_AMP_INFO,
|
||||
READ_LOC_AMP_ASSOC,
|
||||
|
@ -131,16 +129,10 @@ struct a2mp_physlink_rsp {
|
|||
#define A2MP_STATUS_PHYS_LINK_EXISTS 0x05
|
||||
#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);
|
||||
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 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_send_getinfo_rsp(struct hci_dev *hdev);
|
||||
void a2mp_send_getampassoc_rsp(struct hci_dev *hdev, u8 status);
|
||||
|
|
|
@ -309,7 +309,7 @@ void hci_sco_setup(struct hci_conn *conn, __u8 status)
|
|||
else
|
||||
hci_add_sco(sco, conn->handle);
|
||||
} else {
|
||||
hci_proto_connect_cfm(sco, status);
|
||||
hci_connect_cfm(sco, status);
|
||||
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,
|
||||
status);
|
||||
|
||||
hci_proto_connect_cfm(conn, status);
|
||||
hci_connect_cfm(conn, status);
|
||||
|
||||
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;
|
||||
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
|
||||
* established. To be able to handle these ATT messages, the user-
|
||||
* 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;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
acl = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ DEFINE_RWLOCK(hci_dev_list_lock);
|
|||
|
||||
/* HCI callback list */
|
||||
LIST_HEAD(hci_cb_list);
|
||||
DEFINE_RWLOCK(hci_cb_list_lock);
|
||||
DEFINE_MUTEX(hci_cb_list_lock);
|
||||
|
||||
/* HCI ID Numbering */
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
|
@ -400,9 +400,6 @@ static void amp_init(struct hci_request *req)
|
|||
/* Read Local Supported Commands */
|
||||
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 */
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct hci_dev *hdev = req->hdev;
|
||||
|
@ -432,7 +439,7 @@ static void hci_init1_req(struct hci_request *req, unsigned long opt)
|
|||
break;
|
||||
|
||||
case HCI_AMP:
|
||||
amp_init(req);
|
||||
amp_init1(req);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -578,6 +585,9 @@ static void hci_init2_req(struct hci_request *req, unsigned long opt)
|
|||
{
|
||||
struct hci_dev *hdev = req->hdev;
|
||||
|
||||
if (hdev->dev_type == HCI_AMP)
|
||||
return amp_init2(req);
|
||||
|
||||
if (lmp_bredr_capable(hdev))
|
||||
bredr_setup(req);
|
||||
else
|
||||
|
@ -896,17 +906,17 @@ static int __hci_init(struct hci_dev *hdev)
|
|||
&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);
|
||||
if (err < 0)
|
||||
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);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
@ -1591,6 +1601,12 @@ static int hci_dev_do_close(struct hci_dev *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);
|
||||
|
||||
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);
|
||||
|
||||
write_lock(&hci_cb_list_lock);
|
||||
list_add(&cb->list, &hci_cb_list);
|
||||
write_unlock(&hci_cb_list_lock);
|
||||
mutex_lock(&hci_cb_list_lock);
|
||||
list_add_tail(&cb->list, &hci_cb_list);
|
||||
mutex_unlock(&hci_cb_list_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -3460,9 +3476,9 @@ int hci_unregister_cb(struct hci_cb *cb)
|
|||
{
|
||||
BT_DBG("%p name %s", cb, cb->name);
|
||||
|
||||
write_lock(&hci_cb_list_lock);
|
||||
mutex_lock(&hci_cb_list_lock);
|
||||
list_del(&cb->list);
|
||||
write_unlock(&hci_cb_list_lock);
|
||||
mutex_unlock(&hci_cb_list_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,29 @@
|
|||
SOFTWARE IS DISCLAIMED.
|
||||
*/
|
||||
|
||||
#if IS_ENABLED(CONFIG_BT_DEBUGFS)
|
||||
|
||||
void hci_debugfs_create_common(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_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
|
||||
|
|
|
@ -1537,7 +1537,7 @@ static void hci_cs_create_conn(struct hci_dev *hdev, __u8 status)
|
|||
if (conn && conn->state == BT_CONNECT) {
|
||||
if (status != 0x0c || conn->attempt > 2) {
|
||||
conn->state = BT_CLOSED;
|
||||
hci_proto_connect_cfm(conn, status);
|
||||
hci_connect_cfm(conn, status);
|
||||
hci_conn_del(conn);
|
||||
} else
|
||||
conn->state = BT_CONNECT2;
|
||||
|
@ -1581,7 +1581,7 @@ static void hci_cs_add_sco(struct hci_dev *hdev, __u8 status)
|
|||
if (sco) {
|
||||
sco->state = BT_CLOSED;
|
||||
|
||||
hci_proto_connect_cfm(sco, status);
|
||||
hci_connect_cfm(sco, status);
|
||||
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));
|
||||
if (conn) {
|
||||
if (conn->state == BT_CONFIG) {
|
||||
hci_proto_connect_cfm(conn, status);
|
||||
hci_connect_cfm(conn, status);
|
||||
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));
|
||||
if (conn) {
|
||||
if (conn->state == BT_CONFIG) {
|
||||
hci_proto_connect_cfm(conn, status);
|
||||
hci_connect_cfm(conn, status);
|
||||
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));
|
||||
if (conn) {
|
||||
if (conn->state == BT_CONFIG) {
|
||||
hci_proto_connect_cfm(conn, status);
|
||||
hci_connect_cfm(conn, status);
|
||||
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));
|
||||
if (conn) {
|
||||
if (conn->state == BT_CONFIG) {
|
||||
hci_proto_connect_cfm(conn, status);
|
||||
hci_connect_cfm(conn, status);
|
||||
hci_conn_drop(conn);
|
||||
}
|
||||
}
|
||||
|
@ -1873,7 +1873,7 @@ static void hci_cs_setup_sync_conn(struct hci_dev *hdev, __u8 status)
|
|||
if (sco) {
|
||||
sco->state = BT_CLOSED;
|
||||
|
||||
hci_proto_connect_cfm(sco, status);
|
||||
hci_connect_cfm(sco, status);
|
||||
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);
|
||||
|
||||
if (ev->status) {
|
||||
hci_proto_connect_cfm(conn, ev->status);
|
||||
hci_connect_cfm(conn, ev->status);
|
||||
hci_conn_del(conn);
|
||||
} else if (ev->link_type != ACL_LINK)
|
||||
hci_proto_connect_cfm(conn, ev->status);
|
||||
hci_connect_cfm(conn, ev->status);
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
|
@ -2366,7 +2366,7 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
&cp);
|
||||
} else {
|
||||
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;
|
||||
|
||||
hci_proto_disconn_cfm(conn, ev->reason);
|
||||
hci_disconn_cfm(conn, ev->reason);
|
||||
hci_conn_del(conn);
|
||||
|
||||
/* 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);
|
||||
} else {
|
||||
conn->state = BT_CONNECTED;
|
||||
hci_proto_connect_cfm(conn, ev->status);
|
||||
hci_connect_cfm(conn, ev->status);
|
||||
hci_conn_drop(conn);
|
||||
}
|
||||
} 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) &&
|
||||
(!test_bit(HCI_CONN_AES_CCM, &conn->flags) ||
|
||||
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);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
hci_proto_connect_cfm(conn, ev->status);
|
||||
hci_connect_cfm(conn, ev->status);
|
||||
hci_conn_drop(conn);
|
||||
} else
|
||||
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)) {
|
||||
conn->state = BT_CONNECTED;
|
||||
hci_proto_connect_cfm(conn, ev->status);
|
||||
hci_connect_cfm(conn, ev->status);
|
||||
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)) {
|
||||
conn->state = BT_CONNECTED;
|
||||
hci_proto_connect_cfm(conn, ev->status);
|
||||
hci_connect_cfm(conn, ev->status);
|
||||
hci_conn_drop(conn);
|
||||
}
|
||||
|
||||
|
@ -3738,7 +3738,7 @@ static void hci_sync_conn_complete_evt(struct hci_dev *hdev,
|
|||
break;
|
||||
}
|
||||
|
||||
hci_proto_connect_cfm(conn, ev->status);
|
||||
hci_connect_cfm(conn, ev->status);
|
||||
if (ev->status)
|
||||
hci_conn_del(conn);
|
||||
|
||||
|
@ -3849,7 +3849,7 @@ static void hci_key_refresh_complete_evt(struct hci_dev *hdev,
|
|||
if (!ev->status)
|
||||
conn->state = BT_CONNECTED;
|
||||
|
||||
hci_proto_connect_cfm(conn, ev->status);
|
||||
hci_connect_cfm(conn, ev->status);
|
||||
hci_conn_drop(conn);
|
||||
} else {
|
||||
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_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,
|
||||
conn->dst_type);
|
||||
|
|
|
@ -46,9 +46,9 @@ struct hci_pinfo {
|
|||
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 */
|
||||
|
@ -183,12 +183,13 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
kfree_skb(skb_copy);
|
||||
}
|
||||
|
||||
/* Send frame to control socket */
|
||||
void hci_send_to_control(struct sk_buff *skb, struct sock *skip_sk)
|
||||
/* Send frame to sockets with specific channel */
|
||||
void hci_send_to_channel(unsigned short channel, struct sk_buff *skb,
|
||||
struct sock *skip_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);
|
||||
|
||||
|
@ -202,35 +203,7 @@ void hci_send_to_control(struct sk_buff *skb, struct sock *skip_sk)
|
|||
if (sk->sk_state != BT_BOUND)
|
||||
continue;
|
||||
|
||||
if (hci_pi(sk)->channel != HCI_CHANNEL_CONTROL)
|
||||
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)
|
||||
if (hci_pi(sk)->channel != channel)
|
||||
continue;
|
||||
|
||||
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->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);
|
||||
}
|
||||
|
||||
|
@ -397,7 +370,7 @@ void hci_sock_dev_event(struct hci_dev *hdev, int event)
|
|||
|
||||
skb = create_monitor_event(hdev, event);
|
||||
if (skb) {
|
||||
queue_monitor_skb(skb);
|
||||
hci_send_to_channel(HCI_CHANNEL_MONITOR, skb, NULL);
|
||||
kfree_skb(skb);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1244,6 +1244,13 @@ static void l2cap_move_done(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 */
|
||||
chan->conf_state = 0;
|
||||
__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);
|
||||
|
||||
/* 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)
|
||||
goto drop;
|
||||
|
||||
|
@ -7238,13 +7252,16 @@ static struct l2cap_chan *l2cap_global_fixed_chan(struct l2cap_chan *c,
|
|||
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 l2cap_conn *conn;
|
||||
struct l2cap_chan *pchan;
|
||||
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);
|
||||
|
||||
if (status) {
|
||||
|
@ -7307,8 +7324,11 @@ int l2cap_disconn_ind(struct hci_conn *hcon)
|
|||
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);
|
||||
|
||||
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_chan *chan;
|
||||
|
||||
if (!conn)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
|
||||
|
@ -7529,6 +7547,13 @@ drop:
|
|||
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)
|
||||
{
|
||||
struct l2cap_chan *c;
|
||||
|
@ -7570,6 +7595,8 @@ int __init l2cap_init(void)
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
hci_register_cb(&l2cap_cb);
|
||||
|
||||
if (IS_ERR_OR_NULL(bt_debugfs))
|
||||
return 0;
|
||||
|
||||
|
@ -7587,6 +7614,7 @@ int __init l2cap_init(void)
|
|||
void l2cap_exit(void)
|
||||
{
|
||||
debugfs_remove(l2cap_debugfs);
|
||||
hci_unregister_cb(&l2cap_cb);
|
||||
l2cap_cleanup_sockets();
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
#include <net/bluetooth/hci_sock.h>
|
||||
#include <net/bluetooth/l2cap.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 */
|
||||
__net_timestamp(skb);
|
||||
|
||||
hci_send_to_control(skb, skip_sk);
|
||||
hci_send_to_channel(HCI_CHANNEL_CONTROL, skb, skip_sk);
|
||||
kfree_skb(skb);
|
||||
|
||||
return 0;
|
||||
|
@ -2116,8 +2117,7 @@ static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
|
|||
goto failed;
|
||||
}
|
||||
|
||||
if (mgmt_pending_find(MGMT_OP_SET_SSP, hdev) ||
|
||||
mgmt_pending_find(MGMT_OP_SET_HS, hdev)) {
|
||||
if (mgmt_pending_find(MGMT_OP_SET_SSP, hdev)) {
|
||||
err = cmd_status(sk, hdev->id, MGMT_OP_SET_SSP,
|
||||
MGMT_STATUS_BUSY);
|
||||
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);
|
||||
|
||||
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) {
|
||||
changed = !test_and_set_bit(HCI_HS_ENABLED, &hdev->dev_flags);
|
||||
} else {
|
||||
|
@ -3249,6 +3255,10 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||
|
||||
if (PTR_ERR(conn) == -EBUSY)
|
||||
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
|
||||
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);
|
||||
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));
|
||||
|
||||
mgmt_event(MGMT_EV_NEW_CSRK, hdev, &ev, sizeof(ev), NULL);
|
||||
|
|
|
@ -1083,9 +1083,13 @@ int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
|
|||
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);
|
||||
|
||||
if (!status) {
|
||||
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));
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
sco_conn_del(hcon, bt_to_errno(reason));
|
||||
|
@ -1122,6 +1129,12 @@ drop:
|
|||
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)
|
||||
{
|
||||
struct sock *sk;
|
||||
|
@ -1203,6 +1216,8 @@ int __init sco_init(void)
|
|||
|
||||
BT_INFO("SCO socket layer initialized");
|
||||
|
||||
hci_register_cb(&sco_cb);
|
||||
|
||||
if (IS_ERR_OR_NULL(bt_debugfs))
|
||||
return 0;
|
||||
|
||||
|
@ -1222,6 +1237,8 @@ void __exit sco_exit(void)
|
|||
|
||||
debugfs_remove(sco_debugfs);
|
||||
|
||||
hci_unregister_cb(&sco_cb);
|
||||
|
||||
bt_sock_unregister(BTPROTO_SCO);
|
||||
|
||||
proto_unregister(&sco_proto);
|
||||
|
|
|
@ -1252,7 +1252,10 @@ static void smp_distribute_keys(struct smp_chan *smp)
|
|||
|
||||
csrk = kzalloc(sizeof(*csrk), GFP_KERNEL);
|
||||
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));
|
||||
}
|
||||
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);
|
||||
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));
|
||||
}
|
||||
smp->csrk = csrk;
|
||||
|
@ -2951,24 +2957,14 @@ create_chan:
|
|||
l2cap_chan_set_defaults(chan);
|
||||
|
||||
if (cid == L2CAP_CID_SMP) {
|
||||
/* If usage of static address is forced or if the devices
|
||||
* does not have a public address, then listen on the static
|
||||
* address.
|
||||
*
|
||||
* In case BR/EDR has been disabled on a dual-mode controller
|
||||
* 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);
|
||||
u8 bdaddr_type;
|
||||
|
||||
hci_copy_identity_address(hdev, &chan->src, &bdaddr_type);
|
||||
|
||||
if (bdaddr_type == ADDR_LE_DEV_PUBLIC)
|
||||
chan->src_type = BDADDR_LE_PUBLIC;
|
||||
}
|
||||
else
|
||||
chan->src_type = BDADDR_LE_RANDOM;
|
||||
} else {
|
||||
bacpy(&chan->src, &hdev->bdaddr);
|
||||
chan->src_type = BDADDR_BREDR;
|
||||
|
|
|
@ -126,6 +126,7 @@ static void lowpan_setup(struct net_device *dev)
|
|||
dev->header_ops = &lowpan_header_ops;
|
||||
dev->ml_priv = &lowpan_mlme;
|
||||
dev->destructor = free_netdev;
|
||||
dev->features |= NETIF_F_NETNS_LOCAL;
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
if (!tb[IFLA_LINK])
|
||||
if (!tb[IFLA_LINK] ||
|
||||
!net_eq(dev_net(dev), &init_net))
|
||||
return -EINVAL;
|
||||
/* 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)
|
||||
return -ENODEV;
|
||||
if (real_dev->type != ARPHRD_IEEE802154) {
|
||||
|
|
|
@ -225,6 +225,7 @@ static int cfg802154_netdev_notifier_call(struct notifier_block *nb,
|
|||
switch (state) {
|
||||
/* TODO NETDEV_DEVTYPE */
|
||||
case NETDEV_REGISTER:
|
||||
dev->features |= NETIF_F_NETNS_LOCAL;
|
||||
wpan_dev->identifier = ++rdev->wpan_dev_id;
|
||||
list_add_rcu(&wpan_dev->list, &rdev->wpan_dev_list);
|
||||
rdev->devlist_generation++;
|
||||
|
|
Загрузка…
Ссылка в новой задаче