* Re: [PATCH_v3 3/3] android/pan: Handle connection and control state notifications
From: Johan Hedberg @ 2013-11-13 11:16 UTC (permalink / raw)
To: Ravi Kumar Veeramally; +Cc: Szymon Janc, linux-bluetooth
In-Reply-To: <5283506A.4090105@linux.intel.com>
Hi Ravi,
On Wed, Nov 13, 2013, Ravi Kumar Veeramally wrote:
> On 11/13/2013 11:59 AM, Szymon Janc wrote:
> >Hi,
> >
> >>>>+
> >>>>+ switch (opcode) {
> >>>>+ case HAL_EV_PAN_CONN_STATE:
> >>>>+ handle_conn_state(buf);
> >>>>+ break;
> >>>>+ case HAL_EV_PAN_CTRL_STATE:
> >>>>+ handle_ctrl_state(buf);
> >>>>+ break;
> >>>>+ default:
> >>>>+ DBG("Unhandled callback opcode=0x%x", opcode);
> >>>>+ break;
> >>>>+ }
> >>>> }
> >>>What I don't like about this is that you're not pushing the data length
> >>>to the handler functions. If you did that the handler functions could:
> >>>
> >>> if (len < sizeof(*ev))
> >>> return;
> >>>
> >>>Instead of return we could also just abort - what's the general policy
> >>>on the HAL side regarding invalid data from the daemon? How does this
> >>>relate to the work Szymon is doing to add proper checks for the IPC
> >>>data? Is that only for the daemon side?
> >>>
> >>>Are we missing similar checks in other HALs too?
> >> Yes, we are not doing similar checks in other HALs
> >>(hal-bluetooth/a2dp/hid/) too.
> >> Very few places in hal-bluetooth length is passing but validation is
> >>not done.
> >> I will fix them all and send you the patch.
> >As mentioned in my RFC, messages have very similar format and can be checked
> >in single place using some common macros. I'm now working on addressing Johan
> >comments in that RFC. As suggested I'm going to use tables of handlers instead
> >switch-case and this should also allow to refactor current code to avoid
> >switch-cases we currently have. Services will simply register own tables of
> >handlers for service they implement. Idea is that check will be done in common
> >place and then handlers will have form of void handle_foo(struct foo *ev) with
> >guarantee that passed command/event is memory size valid.
> >
> >Plan is to make this generic code use both by hal and daemon.
> >
> >So, I'm not sure if it makes much sense to fix passing buf+len to all handlers
> >we have now.
> >
> >If you have other ideas for this please comment.
> >
> Ok, good to know.
> Johan: Can you now apply my left over patch?
Yes, it's already done, however I'd really like to see the patches to
fix all this asap.
Johan
^ permalink raw reply
* Re: [PATCH_v3 3/3] android/pan: Handle connection and control state notifications
From: Ravi Kumar Veeramally @ 2013-11-13 10:11 UTC (permalink / raw)
To: Szymon Janc, Johan Hedberg; +Cc: linux-bluetooth
In-Reply-To: <1726751.Pm6h7gxZKd@uw000953>
On 11/13/2013 11:59 AM, Szymon Janc wrote:
> Hi,
>
>>>> +
>>>> + switch (opcode) {
>>>> + case HAL_EV_PAN_CONN_STATE:
>>>> + handle_conn_state(buf);
>>>> + break;
>>>> + case HAL_EV_PAN_CTRL_STATE:
>>>> + handle_ctrl_state(buf);
>>>> + break;
>>>> + default:
>>>> + DBG("Unhandled callback opcode=0x%x", opcode);
>>>> + break;
>>>> + }
>>>> }
>>> What I don't like about this is that you're not pushing the data length
>>> to the handler functions. If you did that the handler functions could:
>>>
>>> if (len < sizeof(*ev))
>>> return;
>>>
>>> Instead of return we could also just abort - what's the general policy
>>> on the HAL side regarding invalid data from the daemon? How does this
>>> relate to the work Szymon is doing to add proper checks for the IPC
>>> data? Is that only for the daemon side?
>>>
>>> Are we missing similar checks in other HALs too?
>> Yes, we are not doing similar checks in other HALs
>> (hal-bluetooth/a2dp/hid/) too.
>> Very few places in hal-bluetooth length is passing but validation is
>> not done.
>> I will fix them all and send you the patch.
> As mentioned in my RFC, messages have very similar format and can be checked
> in single place using some common macros. I'm now working on addressing Johan
> comments in that RFC. As suggested I'm going to use tables of handlers instead
> switch-case and this should also allow to refactor current code to avoid
> switch-cases we currently have. Services will simply register own tables of
> handlers for service they implement. Idea is that check will be done in common
> place and then handlers will have form of void handle_foo(struct foo *ev) with
> guarantee that passed command/event is memory size valid.
>
> Plan is to make this generic code use both by hal and daemon.
>
> So, I'm not sure if it makes much sense to fix passing buf+len to all handlers
> we have now.
>
> If you have other ideas for this please comment.
>
Ok, good to know.
Johan: Can you now apply my left over patch?
Thanks,
Ravi.
^ permalink raw reply
* [RFC v4 6/6] Bluetooth: Manually enable or disable 6LoWPAN between devices
From: Jukka Rissanen @ 2013-11-13 10:11 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1384337495-9043-1-git-send-email-jukka.rissanen@linux.intel.com>
This is a temporary patch where user can manually enable or
disable BT 6LoWPAN functionality between devices.
Eventually the connection is established automatically if
the devices are advertising suitable capability and this patch
can be removed.
Before connecting the devices do this
echo 1 > /sys/kernel/debug/bluetooth/hci0/6lowpan
This enables 6LoWPAN support and creates the bt0 interface
automatically when devices are finally connected.
Rebooting or unloading the bluetooth kernel module will also clear the
settings from the kernel.
Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
---
net/bluetooth/6lowpan.c | 105 +++++++++++++++++++++++++++++++++++++++++++++++
net/bluetooth/6lowpan.h | 1 +
net/bluetooth/hci_core.c | 4 ++
3 files changed, 110 insertions(+)
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
index 5f0489c..579a75f 100644
--- a/net/bluetooth/6lowpan.c
+++ b/net/bluetooth/6lowpan.c
@@ -26,6 +26,8 @@
*/
#include <linux/version.h>
+#include <linux/debugfs.h>
+#include <linux/string.h>
#include <linux/bitops.h>
#include <linux/if_arp.h>
#include <linux/netdevice.h>
@@ -1571,6 +1573,109 @@ static struct notifier_block bt_6lowpan_dev_notifier = {
.notifier_call = device_event,
};
+static LIST_HEAD(user_enabled);
+DEFINE_RWLOCK(enabled_lock);
+
+struct lowpan_enabled {
+ __u8 dev_name[HCI_MAX_NAME_LENGTH];
+ bool enabled;
+ struct list_head list;
+};
+
+static int debugfs_show(struct seq_file *f, void *p)
+{
+ struct lowpan_enabled *entry, *tmp;
+ bool found = false;
+
+ write_lock(&enabled_lock);
+ list_for_each_entry_safe(entry, tmp, &user_enabled, list) {
+ seq_printf(f, "%d\n", entry->enabled);
+ found = true;
+ }
+ write_unlock(&enabled_lock);
+
+ if (!found)
+ seq_printf(f, "0\n");
+
+ return 0;
+}
+
+static int debugfs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, debugfs_show, inode->i_private);
+}
+
+static ssize_t writer(struct file *fp, const char __user *user_buffer,
+ size_t count, loff_t *position)
+{
+ struct hci_dev *hdev = fp->f_inode->i_private;
+ struct lowpan_enabled *entry = NULL, *tmp;
+ bool new_value, old_value;
+ char buf[3] = { 0 };
+ ssize_t ret;
+
+ BT_DBG("dev %s count %zd", hdev->dev_name, count);
+
+ ret = simple_write_to_buffer(buf, 2, position, user_buffer, count);
+ if (ret <= 0)
+ return ret;
+
+ if (strtobool(buf, &new_value) < 0)
+ return -EINVAL;
+
+ ret = -ENOENT;
+
+ write_lock(&enabled_lock);
+ list_for_each_entry_safe(entry, tmp, &user_enabled, list) {
+ if (!strncmp(entry->dev_name, hdev->dev_name,
+ HCI_MAX_NAME_LENGTH)) {
+ old_value = entry->enabled;
+ entry->enabled = new_value;
+ ret = 0;
+ break;
+ }
+ }
+ write_unlock(&enabled_lock);
+
+ if (ret == 0 && old_value == new_value)
+ return count;
+
+ if (ret < 0) {
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+
+ strncpy(entry->dev_name, hdev->dev_name, HCI_MAX_NAME_LENGTH);
+ entry->enabled = new_value;
+
+ write_lock(&enabled_lock);
+ INIT_LIST_HEAD(&entry->list);
+ list_add(&entry->list, &user_enabled);
+ write_unlock(&enabled_lock);
+ }
+
+ if (new_value == true)
+ set_bit(HCI_6LOWPAN_ENABLED, &hdev->dev_flags);
+ else
+ clear_bit(HCI_6LOWPAN_ENABLED, &hdev->dev_flags);
+
+ return count;
+}
+
+static const struct file_operations ble_6lowpan_debugfs_fops = {
+ .open = debugfs_open,
+ .read = seq_read,
+ .write = writer,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+void bt_6lowpan_add_debugfs(struct hci_dev *hdev)
+{
+ debugfs_create_file("6lowpan", 0644, hdev->debugfs, hdev,
+ &ble_6lowpan_debugfs_fops);
+}
+
int bt_6lowpan_init(void)
{
return register_netdevice_notifier(&bt_6lowpan_dev_notifier);
diff --git a/net/bluetooth/6lowpan.h b/net/bluetooth/6lowpan.h
index 680eac8..5f60cf2 100644
--- a/net/bluetooth/6lowpan.h
+++ b/net/bluetooth/6lowpan.h
@@ -22,5 +22,6 @@ int bt_6lowpan_add_conn(struct l2cap_conn *conn);
int bt_6lowpan_del_conn(struct l2cap_conn *conn);
int bt_6lowpan_init(void);
void bt_6lowpan_cleanup(void);
+void bt_6lowpan_add_debugfs(struct hci_dev *hdev);
#endif /* __6LOWPAN_H */
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 03e8355..a229ce0 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -34,6 +34,8 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
+#include "6lowpan.h"
+
static void hci_rx_work(struct work_struct *work);
static void hci_cmd_work(struct work_struct *work);
static void hci_tx_work(struct work_struct *work);
@@ -1408,6 +1410,8 @@ static int __hci_init(struct hci_dev *hdev)
hdev, &conn_max_interval_fops);
}
+ bt_6lowpan_add_debugfs(hdev);
+
return 0;
}
--
1.8.3.1
^ permalink raw reply related
* [RFC v4 5/6] Bluetooth: Enable 6LoWPAN if device supports it
From: Jukka Rissanen @ 2013-11-13 10:11 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1384337495-9043-1-git-send-email-jukka.rissanen@linux.intel.com>
Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
---
include/net/bluetooth/hci.h | 1 +
include/net/bluetooth/hci_core.h | 1 +
net/bluetooth/hci_event.c | 3 +++
net/bluetooth/l2cap_core.c | 3 +++
4 files changed, 8 insertions(+)
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 1784c48..f8f8447 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -131,6 +131,7 @@ enum {
HCI_PERIODIC_INQ,
HCI_FAST_CONNECTABLE,
HCI_BREDR_ENABLED,
+ HCI_6LOWPAN_ENABLED,
};
/* A mask for the flags that are supposed to remain when a reset happens
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 8c0ab3d..e689052 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -449,6 +449,7 @@ enum {
HCI_CONN_SSP_ENABLED,
HCI_CONN_POWER_SAVE,
HCI_CONN_REMOTE_OOB,
+ HCI_CONN_6LOWPAN,
};
static inline bool hci_conn_ssp_enabled(struct hci_conn *conn)
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 5fb3df6..5f81245 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -3533,6 +3533,9 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
conn->handle = __le16_to_cpu(ev->handle);
conn->state = BT_CONNECTED;
+ if (test_bit(HCI_6LOWPAN_ENABLED, &hdev->dev_flags))
+ set_bit(HCI_CONN_6LOWPAN, &conn->flags);
+
hci_conn_add_sysfs(conn);
hci_proto_connect_cfm(conn, ev->status);
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 155485f..df475c7 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -6517,6 +6517,9 @@ int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr)
static bool is_bt_6lowpan(struct hci_conn *hcon)
{
+ if (hcon->type == LE_LINK && test_bit(HCI_CONN_6LOWPAN, &hcon->flags))
+ return true;
+
return false;
}
--
1.8.3.1
^ permalink raw reply related
* [RFC v4 4/6] Bluetooth: Enable 6LoWPAN support for BT LE devices
From: Jukka Rissanen @ 2013-11-13 10:11 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1384337495-9043-1-git-send-email-jukka.rissanen@linux.intel.com>
Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
---
net/bluetooth/6lowpan.c | 1042 ++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 1040 insertions(+), 2 deletions(-)
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
index 85754e2..5f0489c 100644
--- a/net/bluetooth/6lowpan.c
+++ b/net/bluetooth/6lowpan.c
@@ -11,6 +11,20 @@
GNU General Public License for more details.
*/
+/*
+ * The compression, uncompression and IPv6 packet code
+ * is from net/ieee802154/6lowpan.c with these copyrights
+ *
+ * Copyright 2011, Siemens AG
+ * written by Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
+ *
+ * Based on patches from Jon Smirl <jonsmirl@gmail.com>
+ * Copyright (c) 2011 Jon Smirl <jonsmirl@gmail.com>
+ *
+ * Jon's code is based on 6lowpan implementation for Contiki which is:
+ * Copyright (c) 2008, Swedish Institute of Computer Science.
+ */
+
#include <linux/version.h>
#include <linux/bitops.h>
#include <linux/if_arp.h>
@@ -172,8 +186,574 @@ static inline void raw_dump_table(const char *caller, char *msg,
16, 1, buf, len, false);
}
-static int recv_pkt(struct sk_buff *skb, struct net_device *dev)
+static inline bool fetch_skb(struct sk_buff *skb,
+ void *data, const unsigned int len)
+{
+ if (unlikely(!pskb_may_pull(skb, len)))
+ return true;
+
+ skb_copy_from_linear_data(skb, data, len);
+ skb_pull(skb, len);
+
+ return false;
+}
+
+/*
+ * Uncompress address function for source and
+ * destination address(non-multicast).
+ *
+ * address_mode is sam value or dam value.
+ */
+static int uncompress_addr(struct sk_buff *skb,
+ struct in6_addr *ipaddr,
+ const u8 address_mode,
+ const u8 *lladdr)
+{
+ bool fail;
+
+ switch (address_mode) {
+ case LOWPAN_IPHC_ADDR_00:
+ /* for global link addresses */
+ fail = fetch_skb(skb, ipaddr->s6_addr, 16);
+ break;
+ case LOWPAN_IPHC_ADDR_01:
+ /* fe:80::XXXX:XXXX:XXXX:XXXX */
+ ipaddr->s6_addr[0] = 0xFE;
+ ipaddr->s6_addr[1] = 0x80;
+ fail = fetch_skb(skb, &ipaddr->s6_addr[8], 8);
+ break;
+ case LOWPAN_IPHC_ADDR_02:
+ /* fe:80::ff:fe00:XXXX */
+ ipaddr->s6_addr[0] = 0xFE;
+ ipaddr->s6_addr[1] = 0x80;
+ ipaddr->s6_addr[11] = 0xFF;
+ ipaddr->s6_addr[12] = 0xFE;
+ fail = fetch_skb(skb, &ipaddr->s6_addr[14], 2);
+ break;
+ case LOWPAN_IPHC_ADDR_03:
+ fail = false;
+ /* XXX: support only normal addr (IEEE802154_ADDR_LONG) atm */
+
+ /* fe:80::XXXX:XXXX:XXXX:XXXX
+ * \_________________/
+ * hwaddr
+ */
+ ipaddr->s6_addr[0] = 0xFE;
+ ipaddr->s6_addr[1] = 0x80;
+ memcpy(&ipaddr->s6_addr[8], lladdr, EUI64_ADDR_LEN);
+ break;
+ default:
+ pr_debug("Invalid address mode value: 0x%x\n", address_mode);
+ return -EINVAL;
+ }
+
+ if (fail) {
+ pr_debug("Failed to fetch skb data\n");
+ return -EIO;
+ }
+
+ raw_dump_inline(NULL, "Reconstructed ipv6 addr is",
+ ipaddr->s6_addr, 16);
+
+ return 0;
+}
+
+/*
+ * Uncompress address function for source context
+ * based address(non-multicast).
+ */
+static int uncompress_context_based_src_addr(struct sk_buff *skb,
+ struct in6_addr *ipaddr,
+ const u8 sam)
+{
+ switch (sam) {
+ case LOWPAN_IPHC_ADDR_00:
+ /* unspec address ::
+ * Do nothing, address is already ::
+ */
+ break;
+ case LOWPAN_IPHC_ADDR_01:
+ /* TODO */
+ case LOWPAN_IPHC_ADDR_02:
+ /* TODO */
+ case LOWPAN_IPHC_ADDR_03:
+ /* TODO */
+ netdev_warn(skb->dev, "SAM value 0x%x not supported\n", sam);
+ return -EINVAL;
+ default:
+ pr_debug("Invalid sam value: 0x%x\n", sam);
+ return -EINVAL;
+ }
+
+ raw_dump_inline(NULL,
+ "Reconstructed context based ipv6 src addr is",
+ ipaddr->s6_addr, 16);
+
+ return 0;
+}
+
+/*
+ * This func is called when the packet from BT LE device
+ * needs to be sent upper layers.
+ */
+static int give_skb_to_upper(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct sk_buff *skb_cp;
+ int ret = NET_RX_SUCCESS;
+
+ skb_cp = skb_copy(skb, GFP_ATOMIC);
+ if (!skb_cp) {
+ ret = -ENOMEM;
+ } else {
+ ret = netif_rx(skb_cp);
+
+ BT_DBG("receive skb %d", ret);
+ if (ret < 0)
+ ret = NET_RX_DROP;
+ }
+
+ return ret;
+}
+
+static inline int fetch_skb_u8(struct sk_buff *skb, u8 *val)
+{
+ if (unlikely(!pskb_may_pull(skb, 1)))
+ return -EINVAL;
+
+ *val = skb->data[0];
+ skb_pull(skb, 1);
+
+ return 0;
+}
+
+static inline int fetch_skb_u16(struct sk_buff *skb, u16 *val)
{
+ if (unlikely(!pskb_may_pull(skb, 2)))
+ return -EINVAL;
+
+ *val = (skb->data[0] << 8) | skb->data[1];
+ skb_pull(skb, 2);
+
+ return 0;
+}
+
+static int skb_deliver(struct sk_buff *skb, struct ipv6hdr *hdr,
+ struct net_device *dev)
+{
+ struct sk_buff *new;
+ int stat;
+
+ new = skb_copy_expand(skb, sizeof(struct ipv6hdr), skb_tailroom(skb),
+ GFP_ATOMIC);
+ kfree_skb(skb);
+
+ if (!new)
+ return -ENOMEM;
+
+ skb_push(new, sizeof(struct ipv6hdr));
+ skb_reset_network_header(new);
+ skb_copy_to_linear_data(new, hdr, sizeof(struct ipv6hdr));
+
+ new->protocol = htons(ETH_P_IPV6);
+ new->pkt_type = PACKET_HOST;
+ new->dev = dev;
+
+ raw_dump_table(__func__,
+ "raw skb data dump before receiving",
+ new->data, new->len);
+
+ stat = give_skb_to_upper(new, dev);
+
+ kfree_skb(new);
+
+ return stat;
+}
+
+/* Uncompress function for multicast destination address,
+ * when M bit is set.
+ */
+static int
+lowpan_uncompress_multicast_daddr(struct sk_buff *skb,
+ struct in6_addr *ipaddr,
+ const u8 dam)
+{
+ bool fail;
+
+ switch (dam) {
+ case LOWPAN_IPHC_DAM_00:
+ /* 00: 128 bits. The full address
+ * is carried in-line.
+ */
+ fail = fetch_skb(skb, ipaddr->s6_addr, 16);
+ break;
+ case LOWPAN_IPHC_DAM_01:
+ /* 01: 48 bits. The address takes
+ * the form ffXX::00XX:XXXX:XXXX.
+ */
+ ipaddr->s6_addr[0] = 0xFF;
+ fail = fetch_skb(skb, &ipaddr->s6_addr[1], 1);
+ fail |= fetch_skb(skb, &ipaddr->s6_addr[11], 5);
+ break;
+ case LOWPAN_IPHC_DAM_10:
+ /* 10: 32 bits. The address takes
+ * the form ffXX::00XX:XXXX.
+ */
+ ipaddr->s6_addr[0] = 0xFF;
+ fail = fetch_skb(skb, &ipaddr->s6_addr[1], 1);
+ fail |= fetch_skb(skb, &ipaddr->s6_addr[13], 3);
+ break;
+ case LOWPAN_IPHC_DAM_11:
+ /* 11: 8 bits. The address takes
+ * the form ff02::00XX.
+ */
+ ipaddr->s6_addr[0] = 0xFF;
+ ipaddr->s6_addr[1] = 0x02;
+ fail = fetch_skb(skb, &ipaddr->s6_addr[15], 1);
+ break;
+ default:
+ pr_debug("DAM value has a wrong value: 0x%x\n", dam);
+ return -EINVAL;
+ }
+
+ if (fail) {
+ pr_debug("Failed to fetch skb data\n");
+ return -EIO;
+ }
+
+ raw_dump_inline(NULL, "Reconstructed ipv6 multicast addr is",
+ ipaddr->s6_addr, 16);
+
+ return 0;
+}
+
+static int
+uncompress_udp_header(struct sk_buff *skb, struct udphdr *uh)
+{
+ u8 tmp;
+
+ if (!uh)
+ goto err;
+
+ if (fetch_skb_u8(skb, &tmp))
+ goto err;
+
+ 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:
+ memcpy(&uh->source, &skb->data[0], 2);
+ memcpy(&uh->dest, &skb->data[2], 2);
+ skb_pull(skb, 4);
+ break;
+ case LOWPAN_NHC_UDP_CS_P_01:
+ memcpy(&uh->source, &skb->data[0], 2);
+ uh->dest =
+ skb->data[2] + LOWPAN_NHC_UDP_8BIT_PORT;
+ skb_pull(skb, 3);
+ break;
+ case LOWPAN_NHC_UDP_CS_P_10:
+ uh->source = skb->data[0] + LOWPAN_NHC_UDP_8BIT_PORT;
+ memcpy(&uh->dest, &skb->data[1], 2);
+ skb_pull(skb, 3);
+ break;
+ case LOWPAN_NHC_UDP_CS_P_11:
+ uh->source =
+ LOWPAN_NHC_UDP_4BIT_PORT + (skb->data[0] >> 4);
+ uh->dest =
+ LOWPAN_NHC_UDP_4BIT_PORT + (skb->data[0] & 0x0f);
+ skb_pull(skb, 1);
+ break;
+ default:
+ pr_debug("ERROR: unknown UDP format\n");
+ goto err;
+ break;
+ }
+
+ pr_debug("uncompressed UDP ports: src = %d, dst = %d\n",
+ uh->source, uh->dest);
+
+ /* copy checksum */
+ memcpy(&uh->check, &skb->data[0], 2);
+ skb_pull(skb, 2);
+
+ /*
+ * UDP lenght 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", uh->len);
+ } else {
+ pr_debug("ERROR: unsupported NH format\n");
+ goto err;
+ }
+
+ return 0;
+err:
+ return -EINVAL;
+}
+
+/* TTL uncompression values */
+static const u8 lowpan_ttl_values[] = { 0, 1, 64, 255 };
+
+static int process_data(struct sk_buff *skb, struct net_device *dev,
+ struct l2cap_conn *conn)
+{
+ struct ipv6hdr hdr = {};
+ u8 tmp, iphc0, iphc1, num_context = 0;
+ const u8 *_saddr = NULL, *_daddr = NULL;
+ struct lowpan_info *info;
+ struct peer *peer;
+ int err;
+
+ raw_dump_table(__func__, "raw skb data dump uncompressed",
+ skb->data, skb->len);
+
+ /* at least two bytes will be used for the encoding */
+ if (skb->len < 2)
+ goto drop;
+
+ if (fetch_skb_u8(skb, &iphc0))
+ goto drop;
+
+ info = lowpan_info(dev);
+
+ if (fetch_skb_u8(skb, &iphc1))
+ goto drop;
+
+ write_lock(&devices_lock);
+ peer = peer_lookup_conn(info, conn);
+ write_unlock(&devices_lock);
+ if (!peer)
+ goto drop;
+
+ _saddr = peer->eui64_addr;
+ _daddr = info->net->dev_addr;
+
+ /* another if the CID flag is set */
+ if (iphc1 & LOWPAN_IPHC_CID) {
+ pr_debug("CID flag is set, increase header with one\n");
+ if (fetch_skb_u8(skb, &num_context))
+ goto drop;
+ }
+
+ hdr.version = 6;
+
+ /* Traffic Class and Flow Label */
+ switch ((iphc0 & LOWPAN_IPHC_TF) >> 3) {
+ /*
+ * Traffic Class and FLow Label carried in-line
+ * ECN + DSCP + 4-bit Pad + Flow Label (4 bytes)
+ */
+ case 0: /* 00b */
+ if (fetch_skb_u8(skb, &tmp))
+ goto drop;
+
+ memcpy(&hdr.flow_lbl, &skb->data[0], 3);
+ skb_pull(skb, 3);
+ hdr.priority = ((tmp >> 2) & 0x0f);
+ hdr.flow_lbl[0] = ((tmp >> 2) & 0x30) | (tmp << 6) |
+ (hdr.flow_lbl[0] & 0x0f);
+ break;
+ /*
+ * Traffic class carried in-line
+ * ECN + DSCP (1 byte), Flow Label is elided
+ */
+ case 2: /* 10b */
+ if (fetch_skb_u8(skb, &tmp))
+ goto drop;
+
+ hdr.priority = ((tmp >> 2) & 0x0f);
+ hdr.flow_lbl[0] = ((tmp << 6) & 0xC0) | ((tmp >> 2) & 0x30);
+ break;
+ /*
+ * Flow Label carried in-line
+ * ECN + 2-bit Pad + Flow Label (3 bytes), DSCP is elided
+ */
+ case 1: /* 01b */
+ if (fetch_skb_u8(skb, &tmp))
+ goto drop;
+
+ hdr.flow_lbl[0] = (skb->data[0] & 0x0F) | ((tmp >> 2) & 0x30);
+ memcpy(&hdr.flow_lbl[1], &skb->data[0], 2);
+ skb_pull(skb, 2);
+ break;
+ /* Traffic Class and Flow Label are elided */
+ case 3: /* 11b */
+ break;
+ default:
+ break;
+ }
+
+ /* Next Header */
+ if ((iphc0 & LOWPAN_IPHC_NH_C) == 0) {
+ /* Next header is carried inline */
+ if (fetch_skb_u8(skb, &(hdr.nexthdr)))
+ goto drop;
+
+ pr_debug("NH flag is set, next header carried inline: %02x\n",
+ hdr.nexthdr);
+ }
+
+ /* Hop Limit */
+ if ((iphc0 & 0x03) != LOWPAN_IPHC_TTL_I)
+ hdr.hop_limit = lowpan_ttl_values[iphc0 & 0x03];
+ else {
+ if (fetch_skb_u8(skb, &(hdr.hop_limit)))
+ goto drop;
+ }
+
+ /* Extract SAM to the tmp variable */
+ tmp = ((iphc1 & LOWPAN_IPHC_SAM) >> LOWPAN_IPHC_SAM_BIT) & 0x03;
+
+ if (iphc1 & LOWPAN_IPHC_SAC) {
+ /* Source address context based uncompression */
+ pr_debug("SAC bit is set. Handle context based source address.\n");
+ err = uncompress_context_based_src_addr(
+ skb, &hdr.saddr, tmp);
+ } else {
+ /* Source address uncompression */
+ pr_debug("source address stateless compression\n");
+ err = uncompress_addr(skb, &hdr.saddr, tmp, _saddr);
+ }
+
+ /* Check on error of previous branch */
+ if (err)
+ goto drop;
+
+ /* Extract DAM to the tmp variable */
+ tmp = ((iphc1 & LOWPAN_IPHC_DAM_11) >> LOWPAN_IPHC_DAM_BIT) & 0x03;
+
+ /* check for Multicast Compression */
+ if (iphc1 & LOWPAN_IPHC_M) {
+ if (iphc1 & LOWPAN_IPHC_DAC) {
+ pr_debug("dest: context-based mcast compression\n");
+ /* TODO: implement this */
+ } else {
+ err = lowpan_uncompress_multicast_daddr(
+ skb, &hdr.daddr, tmp);
+ if (err)
+ goto drop;
+ }
+ } else {
+ err = uncompress_addr(skb, &hdr.daddr, tmp, _daddr);
+ pr_debug("dest: stateless compression mode %d dest %pI6c\n",
+ tmp, &hdr.daddr);
+ if (err)
+ goto drop;
+ }
+
+ /* UDP data uncompression */
+ if (iphc0 & LOWPAN_IPHC_NH_C) {
+ struct udphdr uh;
+ struct sk_buff *new;
+ if (uncompress_udp_header(skb, &uh))
+ goto drop;
+
+ /*
+ * replace the compressed UDP head by the uncompressed UDP
+ * header
+ */
+ new = skb_copy_expand(skb, sizeof(struct udphdr),
+ skb_tailroom(skb), GFP_ATOMIC);
+ kfree_skb(skb);
+
+ if (!new)
+ return -ENOMEM;
+
+ skb = new;
+
+ 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;
+ }
+
+ hdr.payload_len = htons(skb->len);
+
+ pr_debug("skb headroom size = %d, data length = %d\n",
+ skb_headroom(skb), skb->len);
+
+ pr_debug("IPv6 header dump:\n\tversion = %d\n\tlength = %d\n\t"
+ "nexthdr = 0x%02x\n\thop_lim = %d\n\tdest = %pI6c\n",
+ hdr.version, ntohs(hdr.payload_len), hdr.nexthdr,
+ hdr.hop_limit, &hdr.daddr);
+
+ raw_dump_table(__func__, "raw header dump", (u8 *)&hdr,
+ sizeof(hdr));
+
+ return skb_deliver(skb, &hdr, dev);
+
+drop:
+ kfree_skb(skb);
+ return -EINVAL;
+}
+
+static int recv_pkt(struct sk_buff *skb, struct net_device *dev,
+ struct l2cap_conn *conn)
+{
+ struct sk_buff *local_skb;
+
+ if (!netif_running(dev))
+ goto drop;
+
+ if (dev->type != ARPHRD_RAWIP)
+ goto drop;
+
+ /* check that it's our buffer */
+ if (skb->data[0] == LOWPAN_DISPATCH_IPV6) {
+ /* Copy the packet so that the IPv6 header is
+ * properly aligned.
+ */
+ local_skb = skb_copy_expand(skb, NET_SKB_PAD - 1,
+ skb_tailroom(skb), GFP_ATOMIC);
+ if (!local_skb)
+ goto drop;
+
+ local_skb->protocol = htons(ETH_P_IPV6);
+ local_skb->pkt_type = PACKET_HOST;
+
+ skb_reset_network_header(local_skb);
+ skb_set_transport_header(local_skb, sizeof(struct ipv6hdr));
+
+ if (give_skb_to_upper(local_skb, dev) != NET_RX_SUCCESS) {
+ kfree_skb(local_skb);
+ goto drop;
+ }
+
+ dev->stats.rx_bytes += skb->len;
+ dev->stats.rx_packets++;
+
+ kfree_skb(local_skb);
+ kfree_skb(skb);
+ } else {
+ switch (skb->data[0] & 0xe0) {
+ case LOWPAN_DISPATCH_IPHC: /* ipv6 datagram */
+ local_skb = skb_clone(skb, GFP_ATOMIC);
+ if (!local_skb)
+ goto drop;
+ if (process_data(local_skb, dev, conn)
+ != NET_RX_SUCCESS)
+ goto drop;
+
+ dev->stats.rx_bytes += skb->len;
+ dev->stats.rx_packets++;
+
+ kfree_skb(skb);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return NET_RX_SUCCESS;
+
+drop:
kfree_skb(skb);
return NET_RX_DROP;
}
@@ -202,19 +782,472 @@ static void do_send(struct l2cap_conn *conn, struct sk_buff *skb)
BT_DBG("conn %p, skb %p len %d priority %u", conn, skb, skb->len,
skb->priority);
+ hci_send_acl(conn->hchan, skb, ACL_START);
return;
}
+static inline int skbuff_copy(void *msg, int len,
+ int count, int mtu,
+ struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct sk_buff **frag;
+ int sent = 0;
+
+ memcpy(skb_put(skb, count), msg, count);
+
+ sent += count;
+ msg += count;
+ len -= count;
+
+ dev->stats.tx_bytes += count;
+ dev->stats.tx_packets++;
+
+ raw_dump_table(__func__, "Sending", skb->data, skb->len);
+
+ /* Continuation fragments (no L2CAP header) */
+ frag = &skb_shinfo(skb)->frag_list;
+ while (len > 0) {
+ struct sk_buff *tmp;
+
+ count = min_t(unsigned int, mtu, len);
+
+ tmp = bt_skb_alloc(count, GFP_KERNEL);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+
+ *frag = tmp;
+
+ memcpy(skb_put(*frag, count), msg, count);
+
+ raw_dump_table(__func__, "Sending fragment",
+ (*frag)->data, count);
+
+ (*frag)->priority = skb->priority;
+
+ sent += count;
+ msg += count;
+ len -= count;
+
+ skb->len += (*frag)->len;
+ skb->data_len += (*frag)->len;
+
+ frag = &(*frag)->next;
+
+ dev->stats.tx_bytes += count;
+ dev->stats.tx_packets++;
+ }
+
+ return sent;
+}
+
+static struct sk_buff *create_pdu(struct l2cap_conn *conn,
+ void *msg, size_t len,
+ u32 priority,
+ struct net_device *dev)
+{
+ struct sk_buff *skb;
+ int err, count;
+ struct l2cap_hdr *lh;
+
+ if (conn->mtu > (L2CAP_LE_MIN_MTU + L2CAP_HDR_SIZE))
+ /* XXX: This should be not needed and atm is only used for
+ * testing purposes */
+ conn->mtu = L2CAP_LE_MIN_MTU + L2CAP_HDR_SIZE;
+
+ count = min_t(unsigned int, (conn->mtu - L2CAP_HDR_SIZE), len);
+
+ BT_DBG("conn %p len %zu mtu %d count %d", conn, len, conn->mtu, count);
+
+ skb = bt_skb_alloc(count + L2CAP_HDR_SIZE, GFP_KERNEL);
+ if (IS_ERR(skb))
+ return skb;
+
+ skb->priority = priority;
+
+ lh = (struct l2cap_hdr *)skb_put(skb, L2CAP_HDR_SIZE);
+ lh->cid = cpu_to_le16(L2CAP_FC_6LOWPAN);
+ lh->len = cpu_to_le16(len);
+
+ err = skbuff_copy(msg, len, count, conn->mtu, skb, dev);
+ if (unlikely(err < 0)) {
+ kfree_skb(skb);
+ BT_DBG("skbuff copy %d failed", err);
+ return ERR_PTR(err);
+ }
+
+ return skb;
+}
+
static int conn_send(struct l2cap_conn *conn,
void *msg, size_t len, u32 priority,
struct net_device *dev)
{
- struct sk_buff *skb = {0};
+ struct sk_buff *skb;
+
+ skb = create_pdu(conn, msg, len, priority, dev);
+ if (IS_ERR(skb))
+ return -EINVAL;
do_send(conn, skb);
return 0;
}
+static u8 lowpan_compress_addr_64(u8 **hc06_ptr, u8 shift,
+ const struct in6_addr *ipaddr,
+ const unsigned char *lladdr)
+{
+ u8 val = 0;
+
+ if (is_addr_mac_addr_based(ipaddr, lladdr)) {
+ val = 3; /* 0-bits */
+ pr_debug("address compression 0 bits\n");
+ } else if (lowpan_is_iid_16_bit_compressable(ipaddr)) {
+ /* compress IID to 16 bits xxxx::XXXX */
+ memcpy(*hc06_ptr, &ipaddr->s6_addr16[7], 2);
+ *hc06_ptr += 2;
+ val = 2; /* 16-bits */
+ raw_dump_inline(NULL, "Compressed ipv6 addr is (16 bits)",
+ *hc06_ptr - 2, 2);
+ } else {
+ /* do not compress IID => xxxx::IID */
+ memcpy(*hc06_ptr, &ipaddr->s6_addr16[4], 8);
+ *hc06_ptr += 8;
+ val = 1; /* 64-bits */
+ raw_dump_inline(NULL, "Compressed ipv6 addr is (64 bits)",
+ *hc06_ptr - 8, 8);
+ }
+
+ return rol8(val, shift);
+}
+
+static void compress_udp_header(u8 **hc06_ptr, struct sk_buff *skb)
+{
+ struct udphdr *uh = udp_hdr(skb);
+
+ if (((uh->source & LOWPAN_NHC_UDP_4BIT_MASK) ==
+ LOWPAN_NHC_UDP_4BIT_PORT) &&
+ ((uh->dest & LOWPAN_NHC_UDP_4BIT_MASK) ==
+ LOWPAN_NHC_UDP_4BIT_PORT)) {
+ pr_debug("UDP header: both ports compression to 4 bits\n");
+ **hc06_ptr = LOWPAN_NHC_UDP_CS_P_11;
+ **(hc06_ptr + 1) = /* subtraction is faster */
+ (u8)((uh->dest - LOWPAN_NHC_UDP_4BIT_PORT) +
+ ((uh->source & LOWPAN_NHC_UDP_4BIT_PORT) << 4));
+ *hc06_ptr += 2;
+ } else if ((uh->dest & LOWPAN_NHC_UDP_8BIT_MASK) ==
+ LOWPAN_NHC_UDP_8BIT_PORT) {
+ pr_debug("UDP header: remove 8 bits of dest\n");
+ **hc06_ptr = LOWPAN_NHC_UDP_CS_P_01;
+ memcpy(*hc06_ptr + 1, &uh->source, 2);
+ **(hc06_ptr + 3) = (u8)(uh->dest - LOWPAN_NHC_UDP_8BIT_PORT);
+ *hc06_ptr += 4;
+ } else if ((uh->source & LOWPAN_NHC_UDP_8BIT_MASK) ==
+ LOWPAN_NHC_UDP_8BIT_PORT) {
+ pr_debug("UDP header: remove 8 bits of source\n");
+ **hc06_ptr = LOWPAN_NHC_UDP_CS_P_10;
+ memcpy(*hc06_ptr + 1, &uh->dest, 2);
+ **(hc06_ptr + 3) = (u8)(uh->source - LOWPAN_NHC_UDP_8BIT_PORT);
+ *hc06_ptr += 4;
+ } else {
+ pr_debug("UDP header: can't compress\n");
+ **hc06_ptr = LOWPAN_NHC_UDP_CS_P_00;
+ memcpy(*hc06_ptr + 1, &uh->source, 2);
+ memcpy(*hc06_ptr + 3, &uh->dest, 2);
+ *hc06_ptr += 5;
+ }
+
+ /* checksum is always inline */
+ memcpy(*hc06_ptr, &uh->check, 2);
+ *hc06_ptr += 2;
+
+ /* skip the UDP header */
+ skb_pull(skb, sizeof(struct udphdr));
+}
+
+static inline int lowpan_fetch_skb_u8(struct sk_buff *skb, u8 *val)
+{
+ if (unlikely(!pskb_may_pull(skb, 1)))
+ return -EINVAL;
+
+ *val = skb->data[0];
+ skb_pull(skb, 1);
+
+ return 0;
+}
+
+static inline int lowpan_fetch_skb_u16(struct sk_buff *skb, u16 *val)
+{
+ if (unlikely(!pskb_may_pull(skb, 2)))
+ return -EINVAL;
+
+ *val = (skb->data[0] << 8) | skb->data[1];
+ skb_pull(skb, 2);
+
+ return 0;
+}
+
+static void get_dest_bdaddr(struct in6_addr *ip6_daddr,
+ bdaddr_t *addr, u8 *addr_type)
+{
+ u8 *eui64;
+
+ eui64 = ip6_daddr->s6_addr + 8;
+
+ addr->b[0] = eui64[7];
+ addr->b[1] = eui64[6];
+ addr->b[2] = eui64[5];
+ addr->b[3] = eui64[2];
+ addr->b[4] = eui64[1];
+ addr->b[5] = eui64[0];
+
+ addr->b[5] ^= 2;
+
+ /* Set universal/local bit to 0 */
+ if (addr->b[5] & 1) {
+ addr->b[5] &= ~1;
+ *addr_type = BDADDR_LE_PUBLIC;
+ } else
+ *addr_type = BDADDR_LE_RANDOM;
+}
+
+static int header_create(struct sk_buff *skb, struct net_device *dev,
+ unsigned short type, const void *_daddr,
+ const void *_saddr, unsigned int len)
+{
+ u8 tmp, iphc0, iphc1, *hc06_ptr;
+ struct lowpan_info *info;
+ struct peer *peer;
+ struct ipv6hdr *hdr;
+ bdaddr_t addr, *any = BDADDR_ANY;
+ u8 *saddr, *daddr = any->b;
+ u8 addr_type;
+ u8 head[100] = {0};
+
+ if (type != ETH_P_IPV6)
+ return -EINVAL;
+
+ hdr = ipv6_hdr(skb);
+ hc06_ptr = head + 2;
+
+ pr_debug("IPv6 header dump:\n\tversion = %d\n\tlength = %d\n"
+ "\tnexthdr = 0x%02x\n\thop_lim = %d\n\tdest = %pI6c\n",
+ hdr->version, ntohs(hdr->payload_len), hdr->nexthdr,
+ hdr->hop_limit, &hdr->daddr);
+
+ raw_dump_table(__func__, "raw skb network header dump",
+ skb_network_header(skb), sizeof(struct ipv6hdr));
+
+ info = lowpan_info(dev);
+
+ if (ipv6_addr_is_multicast(&hdr->daddr)) {
+ memcpy(&lowpan_cb(skb)->addr, &hdr->daddr,
+ sizeof(struct in6_addr));
+ lowpan_cb(skb)->conn = NULL;
+ } else {
+ /*
+ * Get destination BT device from skb.
+ * If there is no such peer then discard the packet.
+ */
+ get_dest_bdaddr(&hdr->daddr, &addr, &addr_type);
+
+ BT_DBG("dest addr %pMR type %d", &addr, addr_type);
+
+ write_lock(&devices_lock);
+ peer = peer_lookup_ba(info, addr_type, &addr);
+ write_unlock(&devices_lock);
+
+ if (!peer) {
+ BT_DBG("no such peer %pMR found", &addr);
+ return -ENOENT;
+ }
+
+ daddr = peer->eui64_addr;
+
+ memcpy(&lowpan_cb(skb)->addr, &hdr->daddr,
+ sizeof(struct in6_addr));
+ lowpan_cb(skb)->conn = peer->conn;
+ }
+
+ saddr = info->net->dev_addr;
+
+ raw_dump_inline(__func__, "saddr",
+ (unsigned char *)saddr, 8);
+
+ /*
+ * As we copy some bit-length fields, in the IPHC encoding bytes,
+ * we sometimes use |=
+ * If the field is 0, and the current bit value in memory is 1,
+ * this does not work. We therefore reset the IPHC encoding here
+ */
+ iphc0 = LOWPAN_DISPATCH_IPHC;
+ iphc1 = 0;
+
+ /* TODO: context lookup */
+
+ raw_dump_inline(__func__, "daddr",
+ (unsigned char *)daddr, 8);
+
+ raw_dump_table(__func__,
+ "sending raw skb network uncompressed packet",
+ skb->data, skb->len);
+
+ /*
+ * Traffic class, flow label
+ * If flow label is 0, compress it. If traffic class is 0, compress it
+ * We have to process both in the same time as the offset of traffic
+ * class depends on the presence of version and flow label
+ */
+
+ /* hc06 format of TC is ECN | DSCP , original one is DSCP | ECN */
+ tmp = (hdr->priority << 4) | (hdr->flow_lbl[0] >> 4);
+ tmp = ((tmp & 0x03) << 6) | (tmp >> 2);
+
+ if (((hdr->flow_lbl[0] & 0x0F) == 0) &&
+ (hdr->flow_lbl[1] == 0) && (hdr->flow_lbl[2] == 0)) {
+ /* flow label can be compressed */
+ iphc0 |= LOWPAN_IPHC_FL_C;
+ if ((hdr->priority == 0) &&
+ ((hdr->flow_lbl[0] & 0xF0) == 0)) {
+ /* compress (elide) all */
+ iphc0 |= LOWPAN_IPHC_TC_C;
+ } else {
+ /* compress only the flow label */
+ *hc06_ptr = tmp;
+ hc06_ptr += 1;
+ }
+ } else {
+ /* Flow label cannot be compressed */
+ if ((hdr->priority == 0) &&
+ ((hdr->flow_lbl[0] & 0xF0) == 0)) {
+ /* compress only traffic class */
+ iphc0 |= LOWPAN_IPHC_TC_C;
+ *hc06_ptr = (tmp & 0xc0) | (hdr->flow_lbl[0] & 0x0F);
+ memcpy(hc06_ptr + 1, &hdr->flow_lbl[1], 2);
+ hc06_ptr += 3;
+ } else {
+ /* compress nothing */
+ memcpy(hc06_ptr, &hdr, 4);
+ /* replace the top byte with new ECN | DSCP format */
+ *hc06_ptr = tmp;
+ hc06_ptr += 4;
+ }
+ }
+
+ /* 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) {
+ *hc06_ptr = hdr->nexthdr;
+ hc06_ptr += 1;
+ }
+
+ /*
+ * Hop limit
+ * if 1: compress, encoding is 01
+ * if 64: compress, encoding is 10
+ * if 255: compress, encoding is 11
+ * else do not compress
+ */
+ switch (hdr->hop_limit) {
+ case 1:
+ iphc0 |= LOWPAN_IPHC_TTL_1;
+ break;
+ case 64:
+ iphc0 |= LOWPAN_IPHC_TTL_64;
+ break;
+ case 255:
+ iphc0 |= LOWPAN_IPHC_TTL_255;
+ break;
+ default:
+ *hc06_ptr = hdr->hop_limit;
+ hc06_ptr += 1;
+ break;
+ }
+
+ /* source address compression */
+ if (is_addr_unspecified(&hdr->saddr)) {
+ pr_debug("source address is unspecified, setting SAC\n");
+ iphc1 |= LOWPAN_IPHC_SAC;
+ /* TODO: context lookup */
+ } else if (is_addr_link_local(&hdr->saddr)) {
+ iphc1 |= lowpan_compress_addr_64(&hc06_ptr,
+ LOWPAN_IPHC_SAM_BIT, &hdr->saddr, saddr);
+ pr_debug("source address unicast link-local %pI6c "
+ "iphc1 0x%02x\n", &hdr->saddr, iphc1);
+ } else {
+ pr_debug("send the full source address\n");
+ memcpy(hc06_ptr, &hdr->saddr.s6_addr16[0], 16);
+ hc06_ptr += 16;
+ }
+
+ /* destination address compression */
+ if (is_addr_mcast(&hdr->daddr)) {
+ pr_debug("destination address is multicast: ");
+ iphc1 |= LOWPAN_IPHC_M;
+ if (lowpan_is_mcast_addr_compressable8(&hdr->daddr)) {
+ pr_debug("compressed to 1 octet\n");
+ iphc1 |= LOWPAN_IPHC_DAM_11;
+ /* use last byte */
+ *hc06_ptr = hdr->daddr.s6_addr[15];
+ hc06_ptr += 1;
+ } else if (lowpan_is_mcast_addr_compressable32(&hdr->daddr)) {
+ pr_debug("compressed to 4 octets\n");
+ iphc1 |= LOWPAN_IPHC_DAM_10;
+ /* second byte + the last three */
+ *hc06_ptr = hdr->daddr.s6_addr[1];
+ memcpy(hc06_ptr + 1, &hdr->daddr.s6_addr[13], 3);
+ hc06_ptr += 4;
+ } else if (lowpan_is_mcast_addr_compressable48(&hdr->daddr)) {
+ pr_debug("compressed to 6 octets\n");
+ iphc1 |= LOWPAN_IPHC_DAM_01;
+ /* second byte + the last five */
+ *hc06_ptr = hdr->daddr.s6_addr[1];
+ memcpy(hc06_ptr + 1, &hdr->daddr.s6_addr[11], 5);
+ hc06_ptr += 6;
+ } else {
+ pr_debug("using full address\n");
+ iphc1 |= LOWPAN_IPHC_DAM_00;
+ memcpy(hc06_ptr, &hdr->daddr.s6_addr[0], 16);
+ hc06_ptr += 16;
+ }
+ } else {
+ /* TODO: context lookup */
+ if (is_addr_link_local(&hdr->daddr)) {
+ iphc1 |= lowpan_compress_addr_64(&hc06_ptr,
+ LOWPAN_IPHC_DAM_BIT, &hdr->daddr, daddr);
+ pr_debug("dest address unicast link-local %pI6c "
+ "iphc1 0x%02x\n", &hdr->daddr, iphc1);
+ } else {
+ pr_debug("dest address unicast %pI6c\n", &hdr->daddr);
+ memcpy(hc06_ptr, &hdr->daddr.s6_addr16[0], 16);
+ hc06_ptr += 16;
+ }
+ }
+
+ /* UDP header compression */
+ if (hdr->nexthdr == UIP_PROTO_UDP)
+ compress_udp_header(&hc06_ptr, skb);
+
+ head[0] = iphc0;
+ head[1] = iphc1;
+
+ skb_pull(skb, sizeof(struct ipv6hdr));
+
+ memcpy(skb_push(skb, hc06_ptr - head), head, hc06_ptr - head);
+
+ BT_DBG("header len %d skb %u", (int)(hc06_ptr - head), skb->len);
+
+ skb_reset_network_header(skb);
+
+ raw_dump_table(__func__, "raw skb data dump compressed",
+ skb->data, skb->len);
+ return 0;
+}
+
/* Packet to BT LE device */
static int send_pkt(struct l2cap_conn *conn, const void *saddr,
const void *daddr, struct sk_buff *skb,
@@ -305,6 +1338,10 @@ static const struct net_device_ops netdev_ops = {
.ndo_start_xmit = xmit,
};
+static struct header_ops header_ops = {
+ .create = header_create,
+};
+
static void netdev_setup(struct net_device *dev)
{
dev->addr_len = EUI64_ADDR_LEN;
@@ -318,6 +1355,7 @@ static void netdev_setup(struct net_device *dev)
dev->watchdog_timeo = 0;
dev->netdev_ops = &netdev_ops;
+ dev->header_ops = &header_ops;
dev->destructor = free_netdev;
}
--
1.8.3.1
^ permalink raw reply related
* [RFC v4 3/6] Bluetooth: Initial skeleton code for BT 6LoWPAN
From: Jukka Rissanen @ 2013-11-13 10:11 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1384337495-9043-1-git-send-email-jukka.rissanen@linux.intel.com>
Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
---
include/net/bluetooth/l2cap.h | 1 +
net/bluetooth/6lowpan.c | 544 ++++++++++++++++++++++++++++++++++++++++++
net/bluetooth/6lowpan.h | 26 ++
net/bluetooth/Makefile | 2 +-
net/bluetooth/l2cap_core.c | 22 +-
5 files changed, 593 insertions(+), 2 deletions(-)
create mode 100644 net/bluetooth/6lowpan.c
create mode 100644 net/bluetooth/6lowpan.h
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index 5132990..c28ac0d 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -133,6 +133,7 @@ struct l2cap_conninfo {
#define L2CAP_FC_L2CAP 0x02
#define L2CAP_FC_CONNLESS 0x04
#define L2CAP_FC_A2MP 0x08
+#define L2CAP_FC_6LOWPAN 0x3e
/* L2CAP Control Field bit masks */
#define L2CAP_CTRL_SAR 0xC000
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
new file mode 100644
index 0000000..85754e2
--- /dev/null
+++ b/net/bluetooth/6lowpan.c
@@ -0,0 +1,544 @@
+/*
+ Copyright (c) 2013 Intel Corp.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2 and
+ only version 2 as published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+#include <linux/version.h>
+#include <linux/bitops.h>
+#include <linux/if_arp.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#include <net/ipv6.h>
+#include <net/ip6_route.h>
+#include <net/addrconf.h>
+
+#include <net/af_ieee802154.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/l2cap.h>
+
+#include "../ieee802154/6lowpan.h" /* for the compression defines */
+
+#define IFACE_NAME_TEMPLATE "bt%d"
+#define EUI64_ADDR_LEN 8
+
+struct skb_cb {
+ struct in6_addr addr;
+ struct l2cap_conn *conn;
+};
+#define lowpan_cb(skb) ((struct skb_cb *)((skb)->cb))
+
+/*
+ * The devices list contains those devices that we are acting
+ * as a proxy. The BT 6LoWPAN device is a virtual device that
+ * connects to the Bluetooth LE device. The real connection to
+ * BT device is done via l2cap layer. There exists one
+ * virtual device / one BT 6LoWPAN network (=hciX device).
+ * The list contains struct lowpan_dev elements.
+ */
+static LIST_HEAD(bt_6lowpan_devices);
+static DEFINE_RWLOCK(devices_lock);
+
+struct lowpan_dev {
+ struct net_device *dev;
+ struct work_struct delete_netdev;
+ struct list_head list;
+};
+
+struct peer {
+ struct list_head list;
+ struct l2cap_conn *conn;
+
+ /* peer addresses in various formats */
+ unsigned char eui64_addr[EUI64_ADDR_LEN];
+ struct in6_addr peer_addr;
+};
+
+struct lowpan_info {
+ struct net_device *net;
+ struct list_head peers;
+ int peer_count;
+};
+
+static inline struct lowpan_info *lowpan_info(const struct net_device *dev)
+{
+ return netdev_priv(dev);
+}
+
+static inline void peer_add(struct lowpan_info *info, struct peer *peer)
+{
+ list_add(&peer->list, &info->peers);
+ info->peer_count++;
+}
+
+static inline void peer_del(struct lowpan_info *info, struct peer *peer)
+{
+ list_del(&peer->list);
+ info->peer_count--;
+ if (info->peer_count < 0) {
+ BT_ERR("peer count underflow");
+ info->peer_count = 0;
+ }
+}
+
+static inline struct peer *peer_lookup_ba(struct lowpan_info *info,
+ __u8 type, bdaddr_t *ba)
+{
+ struct peer *peer, *tmp;
+
+ BT_DBG("peers %d addr %pMR type %d", info->peer_count, ba, type);
+
+ list_for_each_entry_safe(peer, tmp, &info->peers, list) {
+ BT_DBG("addr %pMR type %d",
+ &peer->conn->hcon->dst, peer->conn->hcon->dst_type);
+
+ if (!bacmp(&peer->conn->hcon->dst, ba))
+ return peer;
+ }
+
+ return NULL;
+}
+
+static inline struct peer *peer_lookup_conn(struct lowpan_info *info,
+ struct l2cap_conn *conn)
+{
+ struct peer *peer, *tmp;
+
+ list_for_each_entry_safe(peer, tmp, &info->peers, list) {
+ if (peer->conn == conn)
+ return peer;
+ }
+
+ return NULL;
+}
+
+static struct peer *lookup_peer(struct l2cap_conn *conn,
+ struct lowpan_info **dev)
+{
+ struct lowpan_dev *entry, *tmp;
+ struct peer *peer = NULL;
+
+ write_lock(&devices_lock);
+
+ list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) {
+ struct lowpan_info *info = lowpan_info(entry->dev);
+
+ if (dev)
+ *dev = info;
+
+ peer = peer_lookup_conn(info, conn);
+ if (peer)
+ break;
+ }
+
+ write_unlock(&devices_lock);
+
+ return peer;
+}
+
+/* print data in line */
+static inline void raw_dump_inline(const char *caller, char *msg,
+ unsigned char *buf, int len)
+{
+ if (msg)
+ pr_debug("%s():%s: ", caller, msg);
+ print_hex_dump_debug("", DUMP_PREFIX_NONE,
+ 16, 1, buf, len, false);
+}
+
+/*
+ * print data in a table format:
+ *
+ * addr: xx xx xx xx xx xx
+ * addr: xx xx xx xx xx xx
+ * ...
+ */
+static inline void raw_dump_table(const char *caller, char *msg,
+ unsigned char *buf, int len)
+{
+ if (msg)
+ pr_debug("%s():%s:\n", caller, msg);
+ print_hex_dump_debug("\t", DUMP_PREFIX_OFFSET,
+ 16, 1, buf, len, false);
+}
+
+static int recv_pkt(struct sk_buff *skb, struct net_device *dev)
+{
+ kfree_skb(skb);
+ return NET_RX_DROP;
+}
+
+/* Packet from BT LE device */
+int bt_6lowpan_recv(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+ struct lowpan_info *info = NULL;
+ struct peer *peer;
+ int err = -ENOENT;
+
+ peer = lookup_peer(conn, &info);
+ if (!peer)
+ return -ENOENT;
+
+ if (info && info->net) {
+ err = recv_pkt(skb, info->net, conn);
+ BT_DBG("recv pkt %d", err);
+ }
+
+ return err;
+}
+
+static void do_send(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+ BT_DBG("conn %p, skb %p len %d priority %u", conn, skb, skb->len,
+ skb->priority);
+
+ return;
+}
+
+static int conn_send(struct l2cap_conn *conn,
+ void *msg, size_t len, u32 priority,
+ struct net_device *dev)
+{
+ struct sk_buff *skb = {0};
+
+ do_send(conn, skb);
+ return 0;
+}
+
+/* Packet to BT LE device */
+static int send_pkt(struct l2cap_conn *conn, const void *saddr,
+ const void *daddr, struct sk_buff *skb,
+ struct net_device *dev)
+{
+ raw_dump_table(__func__, "raw skb data dump before fragmentation",
+ skb->data, skb->len);
+
+ return conn_send(conn, skb->data, skb->len, 0, dev);
+}
+
+static int send_mcast_pkt(struct sk_buff *skb, struct net_device *dev)
+{
+ struct sk_buff *local_skb;
+ struct lowpan_dev *entry, *tmp;
+ int err = 0;
+
+ write_lock(&devices_lock);
+
+ list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) {
+ struct peer *pentry, *ptmp;
+ struct lowpan_info *info;
+
+ if (entry->dev != dev)
+ continue;
+
+ info = lowpan_info(entry->dev);
+
+ list_for_each_entry_safe(pentry, ptmp, &info->peers, list) {
+ local_skb = skb_clone(skb, GFP_ATOMIC);
+
+ err = send_pkt(pentry->conn, dev->dev_addr,
+ pentry->eui64_addr,
+ local_skb, dev);
+
+ kfree_skb(local_skb);
+ }
+ }
+
+ write_unlock(&devices_lock);
+
+ return err;
+}
+
+static netdev_tx_t xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ int err = -ENOENT;
+ unsigned char *eui64_addr;
+ struct lowpan_info *info;
+ struct peer *peer;
+ bdaddr_t addr;
+ u8 addr_type;
+
+ if (ipv6_addr_is_multicast(&lowpan_cb(skb)->addr)) {
+ /*
+ * We need to send the packet to every device
+ * behind this interface.
+ */
+ err = send_mcast_pkt(skb, dev);
+ } else {
+ get_dest_bdaddr(&lowpan_cb(skb)->addr, &addr, &addr_type);
+ eui64_addr = lowpan_cb(skb)->addr.s6_addr + 8;
+ info = lowpan_info(dev);
+
+ write_lock(&devices_lock);
+ peer = peer_lookup_ba(info, addr_type, &addr);
+ write_unlock(&devices_lock);
+
+ BT_DBG("xmit from %s to %pMR (%pI6c), peer %p", dev->name,
+ &addr, &lowpan_cb(skb)->addr, peer);
+
+ if (peer && peer->conn)
+ err = send_pkt(peer->conn,
+ dev->dev_addr,
+ eui64_addr,
+ skb,
+ dev);
+ }
+ dev_kfree_skb(skb);
+
+ if (err)
+ BT_DBG("ERROR: xmit failed (%d)", err);
+
+ return (err < 0) ? NET_XMIT_DROP : err;
+}
+
+static const struct net_device_ops netdev_ops = {
+ .ndo_start_xmit = xmit,
+};
+
+static void netdev_setup(struct net_device *dev)
+{
+ dev->addr_len = EUI64_ADDR_LEN;
+ dev->type = ARPHRD_RAWIP;
+
+ dev->hard_header_len = 0;
+ dev->needed_tailroom = 0;
+ dev->mtu = IPV6_MIN_MTU;
+ dev->tx_queue_len = 0;
+ dev->flags = IFF_RUNNING | IFF_POINTOPOINT;
+ dev->watchdog_timeo = 0;
+
+ dev->netdev_ops = &netdev_ops;
+ dev->destructor = free_netdev;
+}
+
+static struct device_type bt_type = {
+ .name = "bluetooth",
+};
+
+static void set_addr(u8 *eui, u8 *addr, u8 addr_type)
+{
+ /* addr is the BT address in little-endian format */
+ eui[0] = addr[5];
+ eui[1] = addr[4];
+ eui[2] = addr[3];
+ eui[3] = 0xFF;
+ eui[4] = 0xFE;
+ eui[5] = addr[2];
+ eui[6] = addr[1];
+ eui[7] = addr[0];
+
+ eui[0] ^= 2;
+
+ /*
+ * Universal/local bit set, RFC 4291
+ */
+ if (addr_type == BDADDR_LE_PUBLIC)
+ eui[0] |= 1;
+ else
+ eui[0] &= ~1;
+}
+
+static void set_dev_addr(struct net_device *net, bdaddr_t *addr,
+ u8 addr_type)
+{
+ net->addr_assign_type = NET_ADDR_PERM;
+ set_addr(net->dev_addr, addr->b, addr_type);
+ net->dev_addr[0] ^= 2;
+}
+
+static void ifup(struct net_device *net)
+{
+ int err;
+
+ rtnl_lock();
+ err = dev_open(net);
+ if (err < 0)
+ BT_INFO("iface %s cannot be opened (%d)", net->name, err);
+ rtnl_unlock();
+}
+
+/*
+ * This gets called when BT LE 6LoWPAN device is connected. We then
+ * create network device that acts as a proxy between BT LE device
+ * and kernel network stack.
+ */
+int bt_6lowpan_add_conn(struct l2cap_conn *conn)
+{
+ struct lowpan_info *dev = NULL;
+ struct peer *peer = NULL;
+ struct net_device *net;
+ struct lowpan_dev *entry;
+ int err = 0;
+
+ peer = lookup_peer(conn, &dev);
+ if (peer)
+ return -EEXIST;
+
+ /*
+ * If net device exists already, just add route.
+ */
+ if (dev && !peer)
+ goto add_peer;
+
+ net = alloc_netdev(sizeof(struct lowpan_info), IFACE_NAME_TEMPLATE,
+ netdev_setup);
+ if (!net)
+ return -ENOMEM;
+
+ dev = netdev_priv(net);
+ dev->net = net;
+ INIT_LIST_HEAD(&dev->peers);
+
+ set_dev_addr(net, &conn->hcon->src, conn->hcon->src_type);
+
+ net->netdev_ops = &netdev_ops;
+ SET_NETDEV_DEV(net, &conn->hcon->dev);
+ SET_NETDEV_DEVTYPE(net, &bt_type);
+
+ err = register_netdev(net);
+ if (err < 0) {
+ BT_INFO("register_netdev failed %d", err);
+ free_netdev(net);
+ goto out;
+ }
+
+ BT_DBG("ifindex %d peer bdaddr %pMR my addr %pMR",
+ net->ifindex, &conn->hcon->dst, &conn->hcon->src);
+ set_bit(__LINK_STATE_PRESENT, &net->state);
+
+ entry = kzalloc(sizeof(struct lowpan_dev), GFP_KERNEL);
+ if (!entry) {
+ unregister_netdev(net);
+ return -ENOMEM;
+ }
+
+ entry->dev = net;
+
+ write_lock(&devices_lock);
+ INIT_LIST_HEAD(&entry->list);
+ list_add(&entry->list, &bt_6lowpan_devices);
+ write_unlock(&devices_lock);
+
+ ifup(net);
+
+add_peer:
+ peer = kzalloc(sizeof(struct peer), GFP_KERNEL);
+ if (!peer)
+ return -ENOMEM;
+
+ peer->conn = conn;
+ memset(&peer->peer_addr, 0, sizeof(struct in6_addr));
+
+ /* RFC 2464 ch. 5 */
+ peer->peer_addr.s6_addr[0] = 0xFE;
+ peer->peer_addr.s6_addr[1] = 0x80;
+ set_addr((u8 *)&peer->peer_addr.s6_addr + 8, conn->hcon->dst.b,
+ conn->hcon->dst_type);
+
+ memcpy(&peer->eui64_addr, (u8 *)&peer->peer_addr.s6_addr + 8,
+ EUI64_ADDR_LEN);
+
+ write_lock(&devices_lock);
+ INIT_LIST_HEAD(&peer->list);
+ peer_add(dev, peer);
+ write_unlock(&devices_lock);
+
+ netdev_notify_peers(dev->net); /* send neighbour adv at startup */
+
+out:
+ return err;
+}
+
+static void delete_netdev(struct work_struct *work)
+{
+ struct lowpan_dev *entry = container_of(work, struct lowpan_dev,
+ delete_netdev);
+
+ unregister_netdev(entry->dev);
+
+ /* The entry pointer is deleted in device_event() */
+}
+
+int bt_6lowpan_del_conn(struct l2cap_conn *conn)
+{
+ struct lowpan_dev *entry, *tmp;
+ struct lowpan_info *info = NULL;
+ struct peer *peer;
+ int err = -ENOENT;
+
+ write_lock(&devices_lock);
+
+ list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) {
+ info = lowpan_info(entry->dev);
+ peer = peer_lookup_conn(info, conn);
+ if (peer) {
+ peer_del(info, peer);
+ err = 0;
+ break;
+ }
+ }
+
+ write_unlock(&devices_lock);
+
+ if (!err && info && info->peer_count == 0) {
+ /*
+ * This function is called with hci dev lock held which means
+ * that we must delete the netdevice in worker thread.
+ */
+ INIT_WORK(&entry->delete_netdev, delete_netdev);
+ schedule_work(&entry->delete_netdev);
+ }
+
+ return err;
+}
+
+static int device_event(struct notifier_block *unused,
+ unsigned long event, void *ptr)
+{
+ struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+ struct lowpan_dev *entry, *tmp;
+
+ if (dev->type != ARPHRD_RAWIP)
+ return NOTIFY_DONE;
+
+ switch (event) {
+ case NETDEV_UNREGISTER:
+ write_lock(&devices_lock);
+ list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices,
+ list) {
+ if (entry->dev == dev) {
+ list_del(&entry->list);
+ kfree(entry);
+ break;
+ }
+ }
+ write_unlock(&devices_lock);
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block bt_6lowpan_dev_notifier = {
+ .notifier_call = device_event,
+};
+
+int bt_6lowpan_init(void)
+{
+ return register_netdevice_notifier(&bt_6lowpan_dev_notifier);
+}
+
+void bt_6lowpan_cleanup(void)
+{
+ unregister_netdevice_notifier(&bt_6lowpan_dev_notifier);
+}
diff --git a/net/bluetooth/6lowpan.h b/net/bluetooth/6lowpan.h
new file mode 100644
index 0000000..680eac8
--- /dev/null
+++ b/net/bluetooth/6lowpan.h
@@ -0,0 +1,26 @@
+/*
+ Copyright (c) 2013 Intel Corp.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2 and
+ only version 2 as published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+#ifndef __6LOWPAN_H
+#define __6LOWPAN_H
+
+#include <linux/skbuff.h>
+#include <net/bluetooth/l2cap.h>
+
+int bt_6lowpan_recv(struct l2cap_conn *conn, struct sk_buff *skb);
+int bt_6lowpan_add_conn(struct l2cap_conn *conn);
+int bt_6lowpan_del_conn(struct l2cap_conn *conn);
+int bt_6lowpan_init(void);
+void bt_6lowpan_cleanup(void);
+
+#endif /* __6LOWPAN_H */
diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile
index 6a791e7..80cb215 100644
--- a/net/bluetooth/Makefile
+++ b/net/bluetooth/Makefile
@@ -10,6 +10,6 @@ obj-$(CONFIG_BT_HIDP) += hidp/
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
+ a2mp.o amp.o 6lowpan.o
subdir-ccflags-y += -D__CHECK_ENDIAN__
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 4af3821..155485f 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -40,6 +40,7 @@
#include "smp.h"
#include "a2mp.h"
#include "amp.h"
+#include "6lowpan.h"
bool disable_ertm;
@@ -6473,6 +6474,10 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
l2cap_conn_del(conn->hcon, EACCES);
break;
+ case L2CAP_FC_6LOWPAN:
+ bt_6lowpan_recv(conn, skb);
+ break;
+
default:
l2cap_data_channel(conn, cid, skb);
break;
@@ -6510,6 +6515,11 @@ int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr)
return exact ? lm1 : lm2;
}
+static bool is_bt_6lowpan(struct hci_conn *hcon)
+{
+ return false;
+}
+
void l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
{
struct l2cap_conn *conn;
@@ -6518,8 +6528,12 @@ void l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
if (!status) {
conn = l2cap_conn_add(hcon);
- if (conn)
+ if (conn) {
l2cap_conn_ready(conn);
+
+ if (is_bt_6lowpan(hcon))
+ bt_6lowpan_add_conn(conn);
+ }
} else {
l2cap_conn_del(hcon, bt_to_errno(status));
}
@@ -6540,6 +6554,9 @@ void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason)
{
BT_DBG("hcon %p reason %d", hcon, reason);
+ if (is_bt_6lowpan(hcon))
+ bt_6lowpan_del_conn(hcon->l2cap_data);
+
l2cap_conn_del(hcon, bt_to_errno(reason));
}
@@ -6817,11 +6834,14 @@ int __init l2cap_init(void)
l2cap_debugfs = debugfs_create_file("l2cap", 0444, bt_debugfs,
NULL, &l2cap_debugfs_fops);
+ bt_6lowpan_init();
+
return 0;
}
void l2cap_exit(void)
{
+ bt_6lowpan_cleanup();
debugfs_remove(l2cap_debugfs);
l2cap_cleanup_sockets();
}
--
1.8.3.1
^ permalink raw reply related
* [RFC v4 2/6] ipv6: Add checks for RAWIP ARP type
From: Jukka Rissanen @ 2013-11-13 10:11 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1384337495-9043-1-git-send-email-jukka.rissanen@linux.intel.com>
Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
---
net/ipv6/addrconf.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index d6ff126..cde1061 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -1797,6 +1797,7 @@ static int ipv6_generate_eui64(u8 *eui, struct net_device *dev)
return addrconf_ifid_sit(eui, dev);
case ARPHRD_IPGRE:
return addrconf_ifid_gre(eui, dev);
+ case ARPHRD_RAWIP:
case ARPHRD_IEEE802154:
return addrconf_ifid_eui64(eui, dev);
case ARPHRD_IEEE1394:
@@ -2681,7 +2682,8 @@ static void addrconf_dev_config(struct net_device *dev)
(dev->type != ARPHRD_INFINIBAND) &&
(dev->type != ARPHRD_IEEE802154) &&
(dev->type != ARPHRD_IEEE1394) &&
- (dev->type != ARPHRD_TUNNEL6)) {
+ (dev->type != ARPHRD_TUNNEL6) &&
+ (dev->type != ARPHRD_RAWIP)) {
/* Alas, we support only Ethernet autoconfiguration. */
return;
}
--
1.8.3.1
^ permalink raw reply related
* [RFC v4 1/6] net: if_arp: add ARPHRD_RAWIP type
From: Jukka Rissanen @ 2013-11-13 10:11 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1384337495-9043-1-git-send-email-jukka.rissanen@linux.intel.com>
This is used when there is no L2 header before IP header.
Example of this is Bluetooth 6LoWPAN network.
The RAWIP header type value is already used in some Android kernels
so same value is used here in order not to break userspace.
Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
---
include/uapi/linux/if_arp.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/include/uapi/linux/if_arp.h b/include/uapi/linux/if_arp.h
index d7fea34..06fc69f 100644
--- a/include/uapi/linux/if_arp.h
+++ b/include/uapi/linux/if_arp.h
@@ -59,6 +59,7 @@
#define ARPHRD_LAPB 516 /* LAPB */
#define ARPHRD_DDCMP 517 /* Digital's DDCMP protocol */
#define ARPHRD_RAWHDLC 518 /* Raw HDLC */
+#define ARPHRD_RAWIP 530 /* Raw IP */
#define ARPHRD_TUNNEL 768 /* IPIP tunnel */
#define ARPHRD_TUNNEL6 769 /* IP6IP6 tunnel */
--
1.8.3.1
^ permalink raw reply related
* [RFC v4 0/6] Bluetooth LE 6LoWPAN
From: Jukka Rissanen @ 2013-11-13 10:11 UTC (permalink / raw)
To: linux-bluetooth
Hi,
this is 6LoWPAN code for BT LE as described in
http://tools.ietf.org/html/draft-ietf-6lowpan-btle-12
v4:
- removed the route setting code, neighbour discovery
should allow the devices to discover each other
- fix the uncompression of Traffic Class in IPv6 header,
this makes ssh to work between devices over a BT 6lowpan link
- removed setting of /proc conf options, they were useless
and not to be done in kernel module anyway
v3:
- misc changes according to Marcel's comments
- supports multiple connections / interface
- removed unused fragmentation code
- setup 6lowpan connection automatically if enabled via debugfs
The automatic 6lowpan enabling is done by setting
echo 1 > /sys/kernel/debug/bluetooth/hci0/6lowpan
before devices are connected.
v2:
- Change ARPHRD_IEEE802154 to ARPHRD_RAWIP. The generic code
in patches 1 and 2 is also sent to netdev mailing list.
- Sending route exporting patch 5 to netdev ml
- Check private/public BT address and toggle universal/local bit
accordingly in patch 3.
- The virtual interface template name is now shorter (bt%d)
- Various function name renames
- devtype of the interface set to "bluetooth"
v1:
- initial release
TODO:
- Discovery of 6LoWPAN service needs be automatic (UUID support)
- Refactor compression and uncompression code after these are fixed
in net/ieee802154/6lowpan.c. Bluetooth 6LoWPAN should be able to share
that part of the code.
Known issues:
- no UUID handling yet
Cheers,
Jukka
Jukka Rissanen (6):
net: if_arp: add ARPHRD_RAWIP type
ipv6: Add checks for RAWIP ARP type
Bluetooth: Initial skeleton code for BT 6LoWPAN
Bluetooth: Enable 6LoWPAN support for BT LE devices
Bluetooth: Enable 6LoWPAN if device supports it
Bluetooth: Manually enable or disable 6LoWPAN between devices
include/net/bluetooth/hci.h | 1 +
include/net/bluetooth/hci_core.h | 1 +
include/net/bluetooth/l2cap.h | 1 +
include/uapi/linux/if_arp.h | 1 +
net/bluetooth/6lowpan.c | 1687 ++++++++++++++++++++++++++++++++++++++
net/bluetooth/6lowpan.h | 27 +
net/bluetooth/Makefile | 2 +-
net/bluetooth/hci_core.c | 4 +
net/bluetooth/hci_event.c | 3 +
net/bluetooth/l2cap_core.c | 25 +-
net/ipv6/addrconf.c | 4 +-
11 files changed, 1753 insertions(+), 3 deletions(-)
create mode 100644 net/bluetooth/6lowpan.c
create mode 100644 net/bluetooth/6lowpan.h
--
1.8.3.1
^ permalink raw reply
* Re: [PATCH_v3 3/3] android/pan: Handle connection and control state notifications
From: Szymon Janc @ 2013-11-13 9:59 UTC (permalink / raw)
To: Ravi Kumar Veeramally; +Cc: linux-bluetooth, Johan Hedberg
In-Reply-To: <52834A76.4050600@linux.intel.com>
Hi,
> >> +
> >> + switch (opcode) {
> >> + case HAL_EV_PAN_CONN_STATE:
> >> + handle_conn_state(buf);
> >> + break;
> >> + case HAL_EV_PAN_CTRL_STATE:
> >> + handle_ctrl_state(buf);
> >> + break;
> >> + default:
> >> + DBG("Unhandled callback opcode=0x%x", opcode);
> >> + break;
> >> + }
> >> }
> > What I don't like about this is that you're not pushing the data length
> > to the handler functions. If you did that the handler functions could:
> >
> > if (len < sizeof(*ev))
> > return;
> >
> > Instead of return we could also just abort - what's the general policy
> > on the HAL side regarding invalid data from the daemon? How does this
> > relate to the work Szymon is doing to add proper checks for the IPC
> > data? Is that only for the daemon side?
> >
> > Are we missing similar checks in other HALs too?
>
> Yes, we are not doing similar checks in other HALs
> (hal-bluetooth/a2dp/hid/) too.
> Very few places in hal-bluetooth length is passing but validation is
> not done.
> I will fix them all and send you the patch.
As mentioned in my RFC, messages have very similar format and can be checked
in single place using some common macros. I'm now working on addressing Johan
comments in that RFC. As suggested I'm going to use tables of handlers instead
switch-case and this should also allow to refactor current code to avoid
switch-cases we currently have. Services will simply register own tables of
handlers for service they implement. Idea is that check will be done in common
place and then handlers will have form of void handle_foo(struct foo *ev) with
guarantee that passed command/event is memory size valid.
Plan is to make this generic code use both by hal and daemon.
So, I'm not sure if it makes much sense to fix passing buf+len to all handlers
we have now.
If you have other ideas for this please comment.
--
BR
Szymon Janc
^ permalink raw reply
* Re: [PATCH_v3 3/3] android/pan: Handle connection and control state notifications
From: Ravi Kumar Veeramally @ 2013-11-13 9:46 UTC (permalink / raw)
To: linux-bluetooth, Johan Hedberg
In-Reply-To: <20131113093301.GB1749@x220.p-661hnu-f1>
Hi Johan,
On 11/13/2013 11:33 AM, Johan Hedberg wrote:
> Hi Ravi,
>
> On Wed, Nov 13, 2013, Ravi kumar Veeramally wrote:
>> ---
>> android/hal-pan.c | 31 +++++++++++++++++++++++++++++++
>> 1 file changed, 31 insertions(+)
> I've applied the first two patches, but for this one I've got a few
> questions:
>
>> diff --git a/android/hal-pan.c b/android/hal-pan.c
>> index 851c5d2..2bc560e 100644
>> --- a/android/hal-pan.c
>> +++ b/android/hal-pan.c
>> @@ -31,10 +31,41 @@ static bool interface_ready(void)
>> return cbs != NULL;
>> }
>>
>> +static void handle_conn_state(void *buf)
>> +{
>> + struct hal_ev_pan_conn_state *ev = buf;
>> +
>> + if (cbs->connection_state_cb)
>> + cbs->connection_state_cb(ev->state, ev->status,
>> + (bt_bdaddr_t *) ev->bdaddr,
>> + ev->local_role, ev->remote_role);
>> +}
>> +
>> +static void handle_ctrl_state(void *buf)
>> +{
>> + struct hal_ev_pan_ctrl_state *ev = buf;
>> +
>> + if (cbs->control_state_cb)
>> + cbs->control_state_cb(ev->state, ev->status,
>> + ev->local_role, (char *)ev->name);
>> +}
>> +
>> void bt_notify_pan(uint8_t opcode, void *buf, uint16_t len)
>> {
>> if (!interface_ready())
>> return;
>> +
>> + switch (opcode) {
>> + case HAL_EV_PAN_CONN_STATE:
>> + handle_conn_state(buf);
>> + break;
>> + case HAL_EV_PAN_CTRL_STATE:
>> + handle_ctrl_state(buf);
>> + break;
>> + default:
>> + DBG("Unhandled callback opcode=0x%x", opcode);
>> + break;
>> + }
>> }
> What I don't like about this is that you're not pushing the data length
> to the handler functions. If you did that the handler functions could:
>
> if (len < sizeof(*ev))
> return;
>
> Instead of return we could also just abort - what's the general policy
> on the HAL side regarding invalid data from the daemon? How does this
> relate to the work Szymon is doing to add proper checks for the IPC
> data? Is that only for the daemon side?
>
> Are we missing similar checks in other HALs too?
Yes, we are not doing similar checks in other HALs
(hal-bluetooth/a2dp/hid/) too.
Very few places in hal-bluetooth length is passing but validation is
not done.
I will fix them all and send you the patch.
Thanks,
Ravi.
^ permalink raw reply
* Re: [PATCH_v3 3/3] android/pan: Handle connection and control state notifications
From: Johan Hedberg @ 2013-11-13 9:33 UTC (permalink / raw)
To: Ravi kumar Veeramally; +Cc: linux-bluetooth
In-Reply-To: <1384334727-25940-3-git-send-email-ravikumar.veeramally@linux.intel.com>
Hi Ravi,
On Wed, Nov 13, 2013, Ravi kumar Veeramally wrote:
> ---
> android/hal-pan.c | 31 +++++++++++++++++++++++++++++++
> 1 file changed, 31 insertions(+)
I've applied the first two patches, but for this one I've got a few
questions:
> diff --git a/android/hal-pan.c b/android/hal-pan.c
> index 851c5d2..2bc560e 100644
> --- a/android/hal-pan.c
> +++ b/android/hal-pan.c
> @@ -31,10 +31,41 @@ static bool interface_ready(void)
> return cbs != NULL;
> }
>
> +static void handle_conn_state(void *buf)
> +{
> + struct hal_ev_pan_conn_state *ev = buf;
> +
> + if (cbs->connection_state_cb)
> + cbs->connection_state_cb(ev->state, ev->status,
> + (bt_bdaddr_t *) ev->bdaddr,
> + ev->local_role, ev->remote_role);
> +}
> +
> +static void handle_ctrl_state(void *buf)
> +{
> + struct hal_ev_pan_ctrl_state *ev = buf;
> +
> + if (cbs->control_state_cb)
> + cbs->control_state_cb(ev->state, ev->status,
> + ev->local_role, (char *)ev->name);
> +}
> +
> void bt_notify_pan(uint8_t opcode, void *buf, uint16_t len)
> {
> if (!interface_ready())
> return;
> +
> + switch (opcode) {
> + case HAL_EV_PAN_CONN_STATE:
> + handle_conn_state(buf);
> + break;
> + case HAL_EV_PAN_CTRL_STATE:
> + handle_ctrl_state(buf);
> + break;
> + default:
> + DBG("Unhandled callback opcode=0x%x", opcode);
> + break;
> + }
> }
What I don't like about this is that you're not pushing the data length
to the handler functions. If you did that the handler functions could:
if (len < sizeof(*ev))
return;
Instead of return we could also just abort - what's the general policy
on the HAL side regarding invalid data from the daemon? How does this
relate to the work Szymon is doing to add proper checks for the IPC
data? Is that only for the daemon side?
Are we missing similar checks in other HALs too?
Johan
^ permalink raw reply
* Re: [PATCH_v2 1/3] android/hidhost: Handle uhid output and feature events
From: Johan Hedberg @ 2013-11-13 9:27 UTC (permalink / raw)
To: Ravi Kumar Veeramally; +Cc: linux-bluetooth
In-Reply-To: <528343B1.5020306@linux.intel.com>
Hi Ravi,
On Wed, Nov 13, 2013, Ravi Kumar Veeramally wrote:
> Hi Johan,
>
> >>+ sscanf((char *) &(ev->u.output.data)[i * 2],
> >>+ "%hhx", &(req + 1)[i]);
> >The last parameter is a bit of a brain twister. How about simply &req[1 + i]?
> >
> Yeap, simple :).
I fixed this up myself, so all three patches have now been applied.
Johan
^ permalink raw reply
* [PATCH_v3 3/3] android/pan: Handle connection and control state notifications
From: Ravi kumar Veeramally @ 2013-11-13 9:25 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Ravi kumar Veeramally
In-Reply-To: <1384334727-25940-1-git-send-email-ravikumar.veeramally@linux.intel.com>
---
android/hal-pan.c | 31 +++++++++++++++++++++++++++++++
1 file changed, 31 insertions(+)
diff --git a/android/hal-pan.c b/android/hal-pan.c
index 851c5d2..2bc560e 100644
--- a/android/hal-pan.c
+++ b/android/hal-pan.c
@@ -31,10 +31,41 @@ static bool interface_ready(void)
return cbs != NULL;
}
+static void handle_conn_state(void *buf)
+{
+ struct hal_ev_pan_conn_state *ev = buf;
+
+ if (cbs->connection_state_cb)
+ cbs->connection_state_cb(ev->state, ev->status,
+ (bt_bdaddr_t *) ev->bdaddr,
+ ev->local_role, ev->remote_role);
+}
+
+static void handle_ctrl_state(void *buf)
+{
+ struct hal_ev_pan_ctrl_state *ev = buf;
+
+ if (cbs->control_state_cb)
+ cbs->control_state_cb(ev->state, ev->status,
+ ev->local_role, (char *)ev->name);
+}
+
void bt_notify_pan(uint8_t opcode, void *buf, uint16_t len)
{
if (!interface_ready())
return;
+
+ switch (opcode) {
+ case HAL_EV_PAN_CONN_STATE:
+ handle_conn_state(buf);
+ break;
+ case HAL_EV_PAN_CTRL_STATE:
+ handle_ctrl_state(buf);
+ break;
+ default:
+ DBG("Unhandled callback opcode=0x%x", opcode);
+ break;
+ }
}
static bt_status_t pan_enable(int local_role)
--
1.8.3.2
^ permalink raw reply related
* [PATCH_v3 2/3] android/pan: Add notify method to PAN notifications
From: Ravi kumar Veeramally @ 2013-11-13 9:25 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Ravi kumar Veeramally
In-Reply-To: <1384334727-25940-1-git-send-email-ravikumar.veeramally@linux.intel.com>
---
android/hal-pan.c | 6 ++++++
android/hal.h | 1 +
2 files changed, 7 insertions(+)
diff --git a/android/hal-pan.c b/android/hal-pan.c
index bec179f..851c5d2 100644
--- a/android/hal-pan.c
+++ b/android/hal-pan.c
@@ -31,6 +31,12 @@ static bool interface_ready(void)
return cbs != NULL;
}
+void bt_notify_pan(uint8_t opcode, void *buf, uint16_t len)
+{
+ if (!interface_ready())
+ return;
+}
+
static bt_status_t pan_enable(int local_role)
{
struct hal_cmd_pan_enable cmd;
diff --git a/android/hal.h b/android/hal.h
index baa4754..72090fe 100644
--- a/android/hal.h
+++ b/android/hal.h
@@ -31,3 +31,4 @@ void bt_thread_associate(void);
void bt_thread_disassociate(void);
void bt_notify_hidhost(uint8_t opcode, void *buf, uint16_t len);
void bt_notify_a2dp(uint8_t opcode, void *buf, uint16_t len);
+void bt_notify_pan(uint8_t opcode, void *buf, uint16_t len);
--
1.8.3.2
^ permalink raw reply related
* [PATCH_v3 1/3] android: Fix opcode parameter type from uint16_t to uint8_t
From: Ravi kumar Veeramally @ 2013-11-13 9:25 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Ravi kumar Veeramally
---
android/hal-a2dp.c | 2 +-
android/hal-bluetooth.c | 2 +-
android/hal-hidhost.c | 2 +-
android/hal.h | 6 +++---
4 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/android/hal-a2dp.c b/android/hal-a2dp.c
index f0c301e..4fe7c20 100644
--- a/android/hal-a2dp.c
+++ b/android/hal-a2dp.c
@@ -49,7 +49,7 @@ static void handle_audio_state(void *buf)
}
/* will be called from notification thread context */
-void bt_notify_a2dp(uint16_t opcode, void *buf, uint16_t len)
+void bt_notify_a2dp(uint8_t opcode, void *buf, uint16_t len)
{
if (!interface_ready())
return;
diff --git a/android/hal-bluetooth.c b/android/hal-bluetooth.c
index 3e5d41f..1cfd994 100644
--- a/android/hal-bluetooth.c
+++ b/android/hal-bluetooth.c
@@ -281,7 +281,7 @@ static void handle_acl_state_changed(void *buf)
}
/* will be called from notification thread context */
-void bt_notify_adapter(uint16_t opcode, void *buf, uint16_t len)
+void bt_notify_adapter(uint8_t opcode, void *buf, uint16_t len)
{
if (!interface_ready())
return;
diff --git a/android/hal-hidhost.c b/android/hal-hidhost.c
index 97a1aa0..2ce17a3 100644
--- a/android/hal-hidhost.c
+++ b/android/hal-hidhost.c
@@ -88,7 +88,7 @@ static void handle_virtual_unplug(void *buf)
}
/* will be called from notification thread context */
-void bt_notify_hidhost(uint16_t opcode, void *buf, uint16_t len)
+void bt_notify_hidhost(uint8_t opcode, void *buf, uint16_t len)
{
if (!interface_ready())
return;
diff --git a/android/hal.h b/android/hal.h
index 2ce7932..baa4754 100644
--- a/android/hal.h
+++ b/android/hal.h
@@ -26,8 +26,8 @@ bthh_interface_t *bt_get_hidhost_interface(void);
btpan_interface_t *bt_get_pan_interface(void);
btav_interface_t *bt_get_a2dp_interface(void);
-void bt_notify_adapter(uint16_t opcode, void *buf, uint16_t len);
+void bt_notify_adapter(uint8_t opcode, void *buf, uint16_t len);
void bt_thread_associate(void);
void bt_thread_disassociate(void);
-void bt_notify_hidhost(uint16_t opcode, void *buf, uint16_t len);
-void bt_notify_a2dp(uint16_t opcode, void *buf, uint16_t len);
+void bt_notify_hidhost(uint8_t opcode, void *buf, uint16_t len);
+void bt_notify_a2dp(uint8_t opcode, void *buf, uint16_t len);
--
1.8.3.2
^ permalink raw reply related
* Re: [PATCH_v2 1/3] android/hidhost: Handle uhid output and feature events
From: Ravi Kumar Veeramally @ 2013-11-13 9:17 UTC (permalink / raw)
To: linux-bluetooth, Johan Hedberg
In-Reply-To: <20131113090159.GA32659@x220.p-661hnu-f1>
Hi Johan,
>> + sscanf((char *) &(ev->u.output.data)[i * 2],
>> + "%hhx", &(req + 1)[i]);
> The last parameter is a bit of a brain twister. How about simply &req[1 + i]?
>
Yeap, simple :).
Thanks,
Ravi.
^ permalink raw reply
* Re: [PATCH_v2 1/3] android/hidhost: Handle uhid output and feature events
From: Johan Hedberg @ 2013-11-13 9:01 UTC (permalink / raw)
To: Ravi kumar Veeramally; +Cc: linux-bluetooth
In-Reply-To: <1384268835-7570-1-git-send-email-ravikumar.veeramally@linux.intel.com>
Hi Ravi,
On Tue, Nov 12, 2013, Ravi kumar Veeramally wrote:
> + sscanf((char *) &(ev->u.output.data)[i * 2],
> + "%hhx", &(req + 1)[i]);
The last parameter is a bit of a brain twister. How about simply &req[1 + i]?
Johan
^ permalink raw reply
* [RFC BlueZ 3/3] input: Fix setting .local_uuid
From: Luiz Augusto von Dentz @ 2013-11-13 8:53 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1384332813-7891-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
By setting .local_uuid it makes the core do a scrict check on uuids
enabled for the adapter but HID host does not have a record so it
does not require a .local_uuid to be set.
---
profiles/input/manager.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/profiles/input/manager.c b/profiles/input/manager.c
index 689ccdd..b12b43e 100644
--- a/profiles/input/manager.c
+++ b/profiles/input/manager.c
@@ -57,7 +57,6 @@ static void hid_server_remove(struct btd_profile *p,
static struct btd_profile input_profile = {
.name = "input-hid",
- .local_uuid = HID_UUID,
.remote_uuid = HID_UUID,
.auto_connect = true,
--
1.8.3.1
^ permalink raw reply related
* [RFC BlueZ 2/3] audio/A2DP: Set profile .local_uuid properly
From: Luiz Augusto von Dentz @ 2013-11-13 8:53 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1384332813-7891-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
With the changes to the core now it does a strict match of adapter
profiles with .local_uuid to prevent connecting to profiles not
enabled in SDP.
---
profiles/audio/a2dp.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
index 6b3d6b2..0a347e5 100644
--- a/profiles/audio/a2dp.c
+++ b/profiles/audio/a2dp.c
@@ -2008,6 +2008,7 @@ static struct btd_profile a2dp_source_profile = {
.name = "a2dp-source",
.priority = BTD_PROFILE_PRIORITY_MEDIUM,
+ .local_uuid = A2DP_SINK_UUID,
.remote_uuid = A2DP_SOURCE_UUID,
.device_probe = a2dp_source_probe,
.device_remove = a2dp_source_remove,
@@ -2024,6 +2025,7 @@ static struct btd_profile a2dp_sink_profile = {
.name = "a2dp-sink",
.priority = BTD_PROFILE_PRIORITY_MEDIUM,
+ .local_uuid = A2DP_SOURCE_UUID,
.remote_uuid = A2DP_SINK_UUID,
.device_probe = a2dp_sink_probe,
.device_remove = a2dp_sink_remove,
--
1.8.3.1
^ permalink raw reply related
* [RFC BlueZ 1/3] core/device: Only connect profiles which are enabled
From: Luiz Augusto von Dentz @ 2013-11-13 8:53 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1384332813-7891-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Some profile may register their records dynamically e.g. A2DP so the code
has to check if the profile is really available before attempting to
connect.
---
src/device.c | 35 ++++++++++++++++++++++++++++++-----
1 file changed, 30 insertions(+), 5 deletions(-)
diff --git a/src/device.c b/src/device.c
index 9ca457e..60efd62 100644
--- a/src/device.c
+++ b/src/device.c
@@ -1224,11 +1224,25 @@ void device_add_eir_uuids(struct btd_device *dev, GSList *uuids)
DEVICE_INTERFACE, "UUIDs");
}
+static int uuid_cmp(const void *a, const void *b)
+{
+ const sdp_record_t *rec = a;
+ const char *string = b;
+ uuid_t uuid;
+
+ if (bt_string2uuid(&uuid, string) < 0)
+ return -1;
+
+ return sdp_uuid_cmp(&rec->svclass, &uuid);
+}
+
static struct btd_service *find_connectable_service(struct btd_device *dev,
const char *uuid)
{
GSList *l;
+ sdp_list_t *uuids;
+ uuids = btd_adapter_get_services(dev->adapter);
for (l = dev->services; l != NULL; l = g_slist_next(l)) {
struct btd_service *service = l->data;
struct btd_profile *p = btd_service_get_profile(service);
@@ -1236,7 +1250,12 @@ static struct btd_service *find_connectable_service(struct btd_device *dev,
if (!p->connect || !p->remote_uuid)
continue;
- if (strcasecmp(uuid, p->remote_uuid) == 0)
+ if (strcasecmp(uuid, p->remote_uuid))
+ continue;
+
+ /* Check if adapter has the local_uuid enabled */
+ if (!p->local_uuid || sdp_list_find(uuids,
+ (void *) p->local_uuid, uuid_cmp))
return service;
}
@@ -1256,6 +1275,7 @@ static GSList *create_pending_list(struct btd_device *dev, const char *uuid)
struct btd_service *service;
struct btd_profile *p;
GSList *l;
+ sdp_list_t *uuids;
if (uuid) {
service = find_connectable_service(dev, uuid);
@@ -1265,6 +1285,7 @@ static GSList *create_pending_list(struct btd_device *dev, const char *uuid)
return dev->pending;
}
+ uuids = btd_adapter_get_services(dev->adapter);
for (l = dev->services; l != NULL; l = g_slist_next(l)) {
service = l->data;
p = btd_service_get_profile(service);
@@ -1272,14 +1293,18 @@ static GSList *create_pending_list(struct btd_device *dev, const char *uuid)
if (!p->auto_connect)
continue;
- if (g_slist_find(dev->pending, service))
- continue;
-
if (btd_service_get_state(service) !=
BTD_SERVICE_STATE_DISCONNECTED)
continue;
- dev->pending = g_slist_insert_sorted(dev->pending, service,
+ if (g_slist_find(dev->pending, service))
+ continue;
+
+ /* Check if adapter has the local_uuid enabled */
+ if (!p->local_uuid || sdp_list_find(uuids,
+ (void *) p->local_uuid, uuid_cmp))
+ dev->pending = g_slist_insert_sorted(dev->pending,
+ service,
service_prio_cmp);
}
--
1.8.3.1
^ permalink raw reply related
* [RFC BlueZ 0/3] Check if profile local_uuid is enabled
From: Luiz Augusto von Dentz @ 2013-11-13 8:53 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This attempts to solve a problem with certain APIs that are adapter bond
such as Media interface and adapter_service_add as opposed to btd_profile
that is valid for every adapter.
Making Media API to be available on /org/bluez and perhaps tight btd_profile
and adapter_service_add should solve the problem in the long term but since
we cannot just break the API this is probably still necessary.
Luiz Augusto von Dentz (3):
core/device: Only connect profiles which are enabled
audio/A2DP: Set profile .local_uuid properly
input: Fix setting .local_uuid
profiles/audio/a2dp.c | 2 ++
profiles/input/manager.c | 1 -
src/device.c | 35 ++++++++++++++++++++++++++++++-----
3 files changed, 32 insertions(+), 6 deletions(-)
--
1.8.3.1
^ permalink raw reply
* [PATCH RFC] tty_ldisc: add more limits to the @write_wakeup
From: Huang Shijie @ 2013-11-13 7:30 UTC (permalink / raw)
To: gregkh; +Cc: linux-serial, marcel, linux-bluetooth, Huang Shijie
In the uart_handle_cts_change(), uart_write_wakeup() is called after
we call @uart_port->ops->start_tx().
The Documentation/serial/driver tells us:
-----------------------------------------------
start_tx(port)
Start transmitting characters.
Locking: port->lock taken.
Interrupts: locally disabled.
-----------------------------------------------
So when the uart_write_wakeup() is called, the port->lock is taken by
the upper. See the following callstack:
|_ uart_write_wakeup
|_ tty_wakeup
|_ ld->ops->write_wakeup
With the port->lock held, we call the @write_wakeup. Some implemetation of
the @write_wakeup does not notice that the port->lock is held, and it still
tries to send data with uart_write() which will try to grab the prot->lock.
A dead lock occurs, see the following log caught in the Bluetooth by uart:
--------------------------------------------------------------------
BUG: spinlock lockup suspected on CPU#0, swapper/0/0
lock: 0xdc3f4410, .magic: dead4ead, .owner: swapper/0/0, .owner_cpu: 0
CPU: 0 PID: 0 Comm: swapper/0 Tainted: G W 3.10.17-16839-ge4a1bef #1320
[<80014cbc>] (unwind_backtrace+0x0/0x138) from [<8001251c>] (show_stack+0x10/0x14)
[<8001251c>] (show_stack+0x10/0x14) from [<802816ac>] (do_raw_spin_lock+0x108/0x184)
[<802816ac>] (do_raw_spin_lock+0x108/0x184) from [<806a22b0>] (_raw_spin_lock_irqsave+0x54/0x60)
[<806a22b0>] (_raw_spin_lock_irqsave+0x54/0x60) from [<802f5754>] (uart_write+0x38/0xe0)
[<802f5754>] (uart_write+0x38/0xe0) from [<80455270>] (hci_uart_tx_wakeup+0xa4/0x168)
[<80455270>] (hci_uart_tx_wakeup+0xa4/0x168) from [<802dab18>] (tty_wakeup+0x50/0x5c)
[<802dab18>] (tty_wakeup+0x50/0x5c) from [<802f81a4>] (imx_rtsint+0x50/0x80)
[<802f81a4>] (imx_rtsint+0x50/0x80) from [<802f88f4>] (imx_int+0x158/0x17c)
[<802f88f4>] (imx_int+0x158/0x17c) from [<8007abe0>] (handle_irq_event_percpu+0x50/0x194)
[<8007abe0>] (handle_irq_event_percpu+0x50/0x194) from [<8007ad60>] (handle_irq_event+0x3c/0x5c)
--------------------------------------------------------------------
This patch adds more limits to the @write_wakeup, the one who wants to
implemet the @write_wakeup should follow the limits which avoid the deadlock.
Signed-off-by: Huang Shijie <b32955@freescale.com>
---
include/linux/tty_ldisc.h | 5 ++++-
1 files changed, 4 insertions(+), 1 deletions(-)
diff --git a/include/linux/tty_ldisc.h b/include/linux/tty_ldisc.h
index f15c898..539ccc5 100644
--- a/include/linux/tty_ldisc.h
+++ b/include/linux/tty_ldisc.h
@@ -91,7 +91,10 @@
* This function is called by the low-level tty driver to signal
* that line discpline should try to send more characters to the
* low-level driver for transmission. If the line discpline does
- * not have any more data to send, it can just return.
+ * not have any more data to send, it can just return. If the line
+ * discipline does have some data to send, please arise a tasklet
+ * or workqueue to do the real data transfer. Do not send data in
+ * this hook, it may leads to a deadlock.
*
* int (*hangup)(struct tty_struct *)
*
--
1.7.2.rc3
^ permalink raw reply related
* Re: shutdown(3) and bluetooth.
From: Marcel Holtmann @ 2013-11-13 1:58 UTC (permalink / raw)
To: Dave Jones; +Cc: netdev, linux-bluetooth@vger.kernel.org development
In-Reply-To: <20131113002819.GB12615@redhat.com>
Hi Dave,
>>> So it seems it affects both SCO and RFCOMM.
>>>
>>>> What kernel did you run this against? It is a shot in the dark, but can you try linux-next quickly.
>>>> There was a socket related fix for the socket options where we confused RFCOMM vs L2CAP struct sock.
>>>
>>> first noticed it on Linus' latest HEAD, and then reproduced it on 3.11.6
>>> I'll look at linux-next tomorrow.
>>
>> I looked through the code and only call bt_sock_wait_state when SOCK_LINGER and sk_lingertime is set. In that case we actually block until the socket state changes to BT_CLOSED.
>>
>> The only way I see this could happen is if you have a huge linger timeout and confused the socket state before. What is actually the list of system calls that you are throwing at this socket.
>
> Ah. I recently changed some code that's now doing this on every socket at shutdown..
> (simplified cut-n-paste)
>
> struct linger ling = { .l_onoff = FALSE, };
>
> for (i = 0; i < nr_sockets; i++) {
> fd = shm->sockets[i].fd;
> shm->sockets[i].fd = 0;
>
> setsockopt(fd, SOL_SOCKET, SO_LINGER, &ling, sizeof(struct linger));
> shutdown(fd, SHUT_RDWR);
> close(fd);
> }
>
> I could just rip out that linger code completely and just hope that sockets staying in
> TIME_WAIT is good enough. iirc, I added it when after multiple runs, some of the
> weirder protocols would fail to open a socket once a certain number of existing
> sockets had opened, even if they were in SOCK_WAIT
>
> two remaining questions though. That code is setting linger to false. Why would
> that cause the sk_lingertime to be taken into consideration ? And why is this
> only a problem for bluetooth (apparently) ?
we are not touching that part of setsockopt. That is handled by net/core/sock.c and we just check if SOCK_LINGER flag is set and if we have a positive sk_lingertime. So this is a bit suspicious on why this is happening, but I don’t think it is our mistake.
Regards
Marcel
^ permalink raw reply
* Re: shutdown(3) and bluetooth.
From: Dave Jones @ 2013-11-13 0:28 UTC (permalink / raw)
To: Marcel Holtmann; +Cc: netdev, linux-bluetooth@vger.kernel.org development
In-Reply-To: <D8BE686E-E81D-48CD-8D67-2B138191E0CC@holtmann.org>
On Wed, Nov 13, 2013 at 08:37:15AM +0900, Marcel Holtmann wrote:
> > So it seems it affects both SCO and RFCOMM.
> >
> >> What kernel did you run this against? It is a shot in the dark, but can you try linux-next quickly.
> >> There was a socket related fix for the socket options where we confused RFCOMM vs L2CAP struct sock.
> >
> > first noticed it on Linus' latest HEAD, and then reproduced it on 3.11.6
> > I'll look at linux-next tomorrow.
>
> I looked through the code and only call bt_sock_wait_state when SOCK_LINGER and sk_lingertime is set. In that case we actually block until the socket state changes to BT_CLOSED.
>
> The only way I see this could happen is if you have a huge linger timeout and confused the socket state before. What is actually the list of system calls that you are throwing at this socket.
Ah. I recently changed some code that's now doing this on every socket at shutdown..
(simplified cut-n-paste)
struct linger ling = { .l_onoff = FALSE, };
for (i = 0; i < nr_sockets; i++) {
fd = shm->sockets[i].fd;
shm->sockets[i].fd = 0;
setsockopt(fd, SOL_SOCKET, SO_LINGER, &ling, sizeof(struct linger));
shutdown(fd, SHUT_RDWR);
close(fd);
}
I could just rip out that linger code completely and just hope that sockets staying in
TIME_WAIT is good enough. iirc, I added it when after multiple runs, some of the
weirder protocols would fail to open a socket once a certain number of existing
sockets had opened, even if they were in SOCK_WAIT
two remaining questions though. That code is setting linger to false. Why would
that cause the sk_lingertime to be taken into consideration ? And why is this
only a problem for bluetooth (apparently) ?
Dave
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox