Hi,
this v2 patchset moves the 6lowpan functionality from bluetooth.ko
module to a separate bluetooth_6lowpan.ko module. Patches #2, #3,
#7 and #8 are there to support this change.
Patch #1 refactors the l2cap socket handling so that we can deal with
normal memory buffers instead of struct msghdr buffers.
The address type and its relation to U/L bit in MAC address was
incorrect and it is fixed by patch #6.
Additional channel default values are set by patch #4 so that channel
API user does not need to set all of them separately.
Known issues:
If you have a 6lowpan connection so that there is bt0 device and
you have managed to transfer IPv6 packets between devices, then
if you disconnect bluetooth connection and unload the module, the
kernel will print this information
[ 6652.781151] sysfs group c1bbfdf0 not found for kobject 'tx-0'
[ 6652.800927] Modules linked in: bluetooth_6lowpan(-) 6lowpan_iphc rfcomm bnep nfc ecb btusb bluetooth rfkill parport_pc parport snd_intel8x0 snd_ac97_codec ac97_bus
...
[ 6652.958454] Call Trace:
[ 6652.959097] [<c180e9a8>] dump_stack+0x4b/0x75
[ 6652.959931] [<c104557e>] warn_slowpath_common+0x7e/0xa0
[ 6652.961600] [<c11ae727>] ? sysfs_remove_group+0x87/0x90
[ 6652.962556] [<c11ae727>] ? sysfs_remove_group+0x87/0x90
[ 6652.963507] [<c10455d3>] warn_slowpath_fmt+0x33/0x40
[ 6652.964436] [<c11ae727>] sysfs_remove_group+0x87/0x90
[ 6652.965368] [<c171c3ab>] netdev_queue_update_kobjects+0xcb/0x140
[ 6652.966410] [<c171c290>] ? net_rx_queue_update_kobjects+0xd0/0x120
[ 6652.967474] [<c170ca5b>] ? dev_mc_flush+0x2b/0x30
[ 6652.968370] [<c171c45e>] netdev_unregister_kobject+0x3e/0x60
[ 6652.969382] [<c17044fe>] rollback_registered_many+0x18e/0x260
[ 6652.970842] [<c108e1eb>] ? trace_hardirqs_on+0xb/0x10
[ 6652.971682] [<c17045f8>] rollback_registered+0x28/0x40
[ 6652.972605] [<c1705877>] unregister_netdevice_queue+0x47/0x80
[ 6652.973633] [<c17058c9>] unregister_netdev+0x19/0x30
[ 6652.974569] [<d0d96e96>] bt_6lowpan_cleanup+0xd0/0xf1 [bluetooth_6lowpan]
[ 6652.975715] [<c181413d>] ? mutex_unlock+0xd/0x10
[ 6652.976553] [<c10b8052>] SyS_delete_module+0x132/0x1c0
[ 6653.207496] [<c181a6e0>] ? __do_page_fault+0x570/0x570
[ 6653.208447] [<c1061a97>] ? task_work_run+0x97/0xb0
[ 6653.209344] [<c1817418>] syscall_call+0x7/0xb
[ 6653.239854] ---[ end trace 220b56e4c36be0fe ]---
[ 6653.260995] ------------[ cut here ]------------...
...
[ 6653.101471] sysfs group c1ba7988 not found for kobject 'bt0'
...
[ 6653.396054] Call Trace:
[ 6653.400714] [<c180e9a8>] dump_stack+0x4b/0x75
[ 6653.401621] [<c104557e>] warn_slowpath_common+0x7e/0xa0
[ 6653.402588] [<c11ae727>] ? sysfs_remove_group+0x87/0x90
[ 6653.403552] [<c11ae727>] ? sysfs_remove_group+0x87/0x90
[ 6653.404515] [<c10455d3>] warn_slowpath_fmt+0x33/0x40
[ 6653.405765] [<c11ae727>] sysfs_remove_group+0x87/0x90
[ 6653.406588] [<c11ae75a>] sysfs_remove_groups+0x2a/0x40
[ 6653.407536] [<c14fae43>] device_remove_attrs+0x43/0x70
[ 6653.429816] [<c14fb95d>] device_del+0xed/0x180
[ 6653.431629] [<c171c470>] netdev_unregister_kobject+0x50/0x60
[ 6653.432703] [<c17044fe>] rollback_registered_many+0x18e/0x260
[ 6653.433655] [<c108e1eb>] ? trace_hardirqs_on+0xb/0x10
[ 6653.434592] [<c17045f8>] rollback_registered+0x28/0x40
[ 6653.435535] [<c1705877>] unregister_netdevice_queue+0x47/0x80
[ 6653.436552] [<c17058c9>] unregister_netdev+0x19/0x30
[ 6653.437478] [<d0d96e96>] bt_6lowpan_cleanup+0xd0/0xf1 [bluetooth_6lowpan]
[ 6653.438618] [<c181413d>] ? mutex_unlock+0xd/0x10
[ 6653.439503] [<c10b8052>] SyS_delete_module+0x132/0x1c0
[ 6653.446650] [<c181a6e0>] ? __do_page_fault+0x570/0x570
[ 6653.450627] [<c1061a97>] ? task_work_run+0x97/0xb0
[ 6653.451649] [<c1817418>] syscall_call+0x7/0xb
[ 6653.463235] ---[ end trace 220b56e4c36be0ff ]---
The problem looks similar what is described in this bug report
https://bugzilla.kernel.org/show_bug.cgi?id=65281
I am investigating this issue.
Cheers,
Jukka
Jukka Rissanen (8):
Bluetooth: Refactor l2cap_sock_sendmsg() to copy user buffer
Bluetooth: Create callbacks for hci device creation and deletion
Bluetooth: Create callbacks for hcon connect and disconnect
Bluetooth: l2cap: Set more channel defaults
Bluetooth: 6LoWPAN: Use connected oriented channel instead of fixed
one
Bluetooth: 6lowpan: Fix MAC address universal/local bit handling
Bluetooth: 6LoWPAN: Create a kernel module
Bluetooth: 6lowpan: Remove all 6lowpan network devices when module is
unloaded
include/net/bluetooth/hci.h | 1 -
include/net/bluetooth/hci_core.h | 53 +++
include/net/bluetooth/l2cap.h | 5 +-
net/bluetooth/6lowpan.c | 883 +++++++++++++++++++++++++++++----------
net/bluetooth/6lowpan.h | 47 ---
net/bluetooth/Kconfig | 6 +-
net/bluetooth/Makefile | 4 +-
net/bluetooth/a2mp.c | 12 +-
net/bluetooth/hci_core.c | 63 +--
net/bluetooth/hci_event.c | 6 +-
net/bluetooth/l2cap_core.c | 101 +++--
net/bluetooth/l2cap_sock.c | 14 +-
12 files changed, 814 insertions(+), 381 deletions(-)
delete mode 100644 net/bluetooth/6lowpan.h
--
1.8.3.1
Hi Szymon,
>>>>> +static struct sk_buff *bt_6lowpan_chan_alloc_skb_cb(struct l2cap_chan
>>>>> *chan, + unsigned long len, int nb)
>>>>> +{
>>>>> + return bt_skb_alloc(len, GFP_ATOMIC);
>>>>
>>>> Does this have to be GFP_ATOMIC?
>>>
>>> Yep, I see "BUG: sleeping function called from invalid context..."
>>> without it.
>>
>> should we fix the caller? I am curious why this is possible in the first
>> place. The whole Bluetooth subsystem should run in a workqueue these days.
>> Do we have by any chance the wrong lock hold at this point or why is this
>> happening.
>>
>> If we really need to keep GFP_ATOMIC here then you need to add a comment on
>> why that is. Because otherwise we will forget about it next time around.
>
> FWIW this is due to holding hci_dev_list_lock which is RW_LOCK. For similar
> reason there is GFP_ATOMIC in read_index_list() in mgmt.c.
okay then, any reason this can not be turned into a mutex?
Regards
Marcel
Hi,
On Monday 26 May 2014 14:46:37 Marcel Holtmann wrote:
> Hi Jukka,
>
> >>> +static struct sk_buff *bt_6lowpan_chan_alloc_skb_cb(struct l2cap_chan
> >>> *chan, + unsigned long len, int nb)
> >>> +{
> >>> + return bt_skb_alloc(len, GFP_ATOMIC);
> >>
> >> Does this have to be GFP_ATOMIC?
> >
> > Yep, I see "BUG: sleeping function called from invalid context..."
> > without it.
>
> should we fix the caller? I am curious why this is possible in the first
> place. The whole Bluetooth subsystem should run in a workqueue these days.
> Do we have by any chance the wrong lock hold at this point or why is this
> happening.
>
> If we really need to keep GFP_ATOMIC here then you need to add a comment on
> why that is. Because otherwise we will forget about it next time around.
FWIW this is due to holding hci_dev_list_lock which is RW_LOCK. For similar
reason there is GFP_ATOMIC in read_index_list() in mgmt.c.
--
Szymon K. Janc
[email protected]
Hi Jukka,
>>> +static struct sk_buff *bt_6lowpan_chan_alloc_skb_cb(struct l2cap_chan *chan,
>>> + unsigned long len, int nb)
>>> +{
>>> + return bt_skb_alloc(len, GFP_ATOMIC);
>>
>> Does this have to be GFP_ATOMIC?
>>
>
> Yep, I see "BUG: sleeping function called from invalid context..."
> without it.
should we fix the caller? I am curious why this is possible in the first place. The whole Bluetooth subsystem should run in a workqueue these days. Do we have by any chance the wrong lock hold at this point or why is this happening.
If we really need to keep GFP_ATOMIC here then you need to add a comment on why that is. Because otherwise we will forget about it next time around.
Regards
Marcel
Hi Marcel,
On la, 2014-05-24 at 22:04 -0700, Marcel Holtmann wrote:
> > +static struct sk_buff *bt_6lowpan_chan_alloc_skb_cb(struct l2cap_chan *chan,
> > + unsigned long len, int nb)
> > +{
> > + return bt_skb_alloc(len, GFP_ATOMIC);
>
> Does this have to be GFP_ATOMIC?
>
Yep, I see "BUG: sleeping function called from invalid context..."
without it.
Cheers,
Jukka
Hi Jukka,
>>> diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
>>> index f59e00c..7e7b28a 100644
>>> --- a/net/bluetooth/l2cap_sock.c
>>> +++ b/net/bluetooth/l2cap_sock.c
>>> @@ -948,6 +948,7 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
>>> {
>>> struct sock *sk = sock->sk;
>>> struct l2cap_chan *chan = l2cap_pi(sk)->chan;
>>> + unsigned char *buf;
>>> int err;
>>>
>>> BT_DBG("sock %p, sk %p", sock, sk);
>>> @@ -968,10 +969,21 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
>>> if (err)
>>> return err;
>>>
>>> + buf = kmalloc(len, GFP_KERNEL);
>>> + if (!buf)
>>> + return -ENOMEM;
>>> +
>>> + if (memcpy_fromiovec(buf, msg->msg_iov, len)) {
>>> + err = -EFAULT;
>>> + goto done;
>>> + }
>>> +
>>> l2cap_chan_lock(chan);
>>> - err = l2cap_chan_send(chan, msg, len, sk->sk_priority);
>>> + err = l2cap_chan_send(chan, buf, len, sk->sk_priority, msg->msg_flags);
>>> l2cap_chan_unlock(chan);
>>>
>>> +done:
>>> + kfree(buf);
>>> return err;
>>> }
>>
>> We don?t have to use chan->ops->alloc_skb here? Has this become obsolete now?
>
> Isn't the buf here just a normal memory buffer that stores just the user
> data and has nothing to do with sk_buff that is returned by alloc_skb()?
I asked the question, because I do not know. Do we need alloc_skb callback at all.
Regards
Marcel
Hi Marcel,
On la, 2014-05-24 at 21:48 -0700, Marcel Holtmann wrote:
> > diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
> > index f59e00c..7e7b28a 100644
> > --- a/net/bluetooth/l2cap_sock.c
> > +++ b/net/bluetooth/l2cap_sock.c
> > @@ -948,6 +948,7 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
> > {
> > struct sock *sk = sock->sk;
> > struct l2cap_chan *chan = l2cap_pi(sk)->chan;
> > + unsigned char *buf;
> > int err;
> >
> > BT_DBG("sock %p, sk %p", sock, sk);
> > @@ -968,10 +969,21 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
> > if (err)
> > return err;
> >
> > + buf = kmalloc(len, GFP_KERNEL);
> > + if (!buf)
> > + return -ENOMEM;
> > +
> > + if (memcpy_fromiovec(buf, msg->msg_iov, len)) {
> > + err = -EFAULT;
> > + goto done;
> > + }
> > +
> > l2cap_chan_lock(chan);
> > - err = l2cap_chan_send(chan, msg, len, sk->sk_priority);
> > + err = l2cap_chan_send(chan, buf, len, sk->sk_priority, msg->msg_flags);
> > l2cap_chan_unlock(chan);
> >
> > +done:
> > + kfree(buf);
> > return err;
> > }
>
> We don’t have to use chan->ops->alloc_skb here? Has this become obsolete now?
Isn't the buf here just a normal memory buffer that stores just the user
data and has nothing to do with sk_buff that is returned by alloc_skb()?
Cheers,
Jukka
Hi Jukka,
> Signed-off-by: Jukka Rissanen <[email protected]>
> ---
> net/bluetooth/6lowpan.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 54 insertions(+)
I am really not looking at patches that do not have a commit message body that explains what is going on.
Regards
Marcel
Hi Jukka,
> Instead of adding the 6LoWPAN functionality to Bluetooth module,
> we create a separate kernel module for it.
>
> Usage:
>
> In the client side do this:
>
> # modprobe bluetooth_6lowpan
> # echo 'psm 62' > /sys/kernel/debug/bluetooth/hci0/6lowpan
> # hciconfig hci0 leadv
>
>
> In the server side do this:
>
> # modprobe bluetooth_6lowpan
> # echo 'psm 62' > /sys/kernel/debug/bluetooth/hci0/6lowpan
> # hcitool lecc E0:06:E6:B7:2A:73
> # echo 'connect E0:06:E6:B7:2A:73 1' > \
> /sys/kernel/debug/bluetooth/hci0/6lowpan
since we have connect/disconnect in the 6loWPAN debugfs control file, the lecc should not be needed at all. Actually it should all work like we do with the socket interface. The underlying LE link should be created automatically for us.
Unless we really go for an API where we just configure the addresses that should enable 6loWPAN and then they get auto-connected once we get a connection on the LE link layer. In that case using add/remove seem the more appropriate control commands.
Something you might want to explore and come back with what would make sense for actual use cases.
> The 6LoWPAN functionality can be controlled by psm value. If it
> is left to 0, then the module is disabled and all the 6LoWPAN
> connections are dropped if there were any.
>
> The 6lowpan controlling interface is a temporary solution
> until the specifications are ready.
>
> Signed-off-by: Jukka Rissanen <[email protected]>
> ---
> net/bluetooth/6lowpan.c | 13 +++++++++++--
> net/bluetooth/Kconfig | 6 +++---
> net/bluetooth/Makefile | 4 +++-
> 3 files changed, 17 insertions(+), 6 deletions(-)
>
> diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
> index 46cc298..04e0501 100644
> --- a/net/bluetooth/6lowpan.c
> +++ b/net/bluetooth/6lowpan.c
> @@ -14,6 +14,7 @@
> #include <linux/if_arp.h>
> #include <linux/netdevice.h>
> #include <linux/etherdevice.h>
> +#include <linux/module.h>
> #include <linux/debugfs.h>
>
> #include <net/ipv6.h>
> @@ -1223,14 +1224,14 @@ static struct hci_cb lowpan_cb = {
> .connect_cfm = lowpan_connect_hcon,
> };
>
> -int bt_6lowpan_init(void)
> +static int __init bt_6lowpan_init(void)
> {
> hci_register_cb(&lowpan_cb);
>
> return register_netdevice_notifier(&bt_6lowpan_dev_notifier);
> }
>
> -void bt_6lowpan_cleanup(void)
> +static void __exit bt_6lowpan_cleanup(void)
> {
> debugfs_remove(lowpan_debugfs);
>
> @@ -1238,3 +1239,11 @@ void bt_6lowpan_cleanup(void)
>
> unregister_netdevice_notifier(&bt_6lowpan_dev_notifier);
> }
> +
> +module_init(bt_6lowpan_init);
> +module_exit(bt_6lowpan_cleanup);
> +
> +MODULE_AUTHOR("Jukka Rissanen <[email protected]>");
> +MODULE_DESCRIPTION("Bluetooth LE 6LoWPAN");
> +MODULE_VERSION(VERSION);
> +MODULE_LICENSE("GPL");
> diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig
> index 06ec144..7ef1a5a 100644
> --- a/net/bluetooth/Kconfig
> +++ b/net/bluetooth/Kconfig
> @@ -6,7 +6,6 @@ menuconfig BT
> tristate "Bluetooth subsystem support"
> depends on NET && !S390
> depends on RFKILL || !RFKILL
> - select 6LOWPAN_IPHC if BT_6LOWPAN
> select CRC16
> select CRYPTO
> select CRYPTO_BLKCIPHER
> @@ -41,10 +40,11 @@ menuconfig BT
> more information, see <http://www.bluez.org/>.
>
> config BT_6LOWPAN
> - bool "Bluetooth 6LoWPAN support"
> + tristate "Bluetooth LE 6LoWPAN support?
Remove the LE bit out of the description. Just Bluetooth 6loWPAN support is good enough.
> depends on BT && IPV6
> + select 6LOWPAN_IPHC if BT_6LOWPAN
> help
> - IPv6 compression over Bluetooth.
> + IPv6 compression over Bluetooth LE.
Here you need to spell our Bluetooth Low Energy.
>
> source "net/bluetooth/rfcomm/Kconfig"
>
> diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile
> index ca51246..886e9aa 100644
> --- a/net/bluetooth/Makefile
> +++ b/net/bluetooth/Makefile
> @@ -7,10 +7,12 @@ obj-$(CONFIG_BT_RFCOMM) += rfcomm/
> obj-$(CONFIG_BT_BNEP) += bnep/
> obj-$(CONFIG_BT_CMTP) += cmtp/
> obj-$(CONFIG_BT_HIDP) += hidp/
> +obj-$(CONFIG_BT_6LOWPAN) += bluetooth_6lowpan.o
> +
> +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
> -bluetooth-$(CONFIG_BT_6LOWPAN) += 6lowpan.o
>
> subdir-ccflags-y += -D__CHECK_ENDIAN__
Regards
Marcel
Hi Jukka,
> The universal/local bit handling was incorrectly done in the code.
>
> So when setting EUI address from BD address we do this:
> - If BD address type is PUBLIC, then we clear the universal bit
> in EUI address. If the address type is RANDOM, then the universal
> bit is set (BT 6lowpan draft chapter 3.2.2)
> - After this we invert the universal/local bit according to RFC 2464
>
> When figuring out BD address we do the reverse:
> - Take EUI address from stateless IPv6 address, invert the
> universal/local bit according to RFC 2464
> - If universal bit is 1 in this modified EUI address, then address
> type is set to RANDOM, otherwise it is PUBLIC
>
> Note that 6lowpan_iphc.[ch] does the final toggling of U/L bit
> before sending or receiving the network packet.
is this broken in the current code as well? We might want to fix it there first.
Regards
Marcel
Hi Jukka,
> Create a CoC dynamically instead of one fixed channel for communication
> to peer devices.
>
> Signed-off-by: Jukka Rissanen <[email protected]>
> ---
> include/net/bluetooth/hci.h | 1 -
> include/net/bluetooth/l2cap.h | 1 -
> net/bluetooth/6lowpan.c | 757 +++++++++++++++++++++++++++++++-----------
> net/bluetooth/6lowpan.h | 47 ---
> net/bluetooth/hci_core.c | 46 +--
> net/bluetooth/hci_event.c | 3 -
> net/bluetooth/l2cap_core.c | 19 +-
> 7 files changed, 575 insertions(+), 299 deletions(-)
> delete mode 100644 net/bluetooth/6lowpan.h
>
> diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
> index 16587dc..3f95aba 100644
> --- a/include/net/bluetooth/hci.h
> +++ b/include/net/bluetooth/hci.h
> @@ -139,7 +139,6 @@ enum {
> HCI_PERIODIC_INQ,
> HCI_FAST_CONNECTABLE,
> HCI_BREDR_ENABLED,
> - HCI_6LOWPAN_ENABLED,
> HCI_LE_SCAN_INTERRUPTED,
> };
we also need to make sure to remove the flag from hci_conn.
>
> diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
> index 3980b81..510c6ab 100644
> --- a/include/net/bluetooth/l2cap.h
> +++ b/include/net/bluetooth/l2cap.h
> @@ -137,7 +137,6 @@ struct l2cap_conninfo {
> #define L2CAP_FC_L2CAP 0x02
> #define L2CAP_FC_CONNLESS 0x04
> #define L2CAP_FC_A2MP 0x08
> -#define L2CAP_FC_6LOWPAN 0x3e /* reserved and temporary value */
>
> /* L2CAP Control Field bit masks */
> #define L2CAP_CTRL_SAR 0xC000
> diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
> index adb3ea0..3390b7b 100644
> --- a/net/bluetooth/6lowpan.c
> +++ b/net/bluetooth/6lowpan.c
> @@ -1,5 +1,5 @@
> /*
> - Copyright (c) 2013 Intel Corp.
> + Copyright (c) 2013-2014 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
> @@ -14,6 +14,7 @@
> #include <linux/if_arp.h>
> #include <linux/netdevice.h>
> #include <linux/etherdevice.h>
> +#include <linux/debugfs.h>
>
> #include <net/ipv6.h>
> #include <net/ip6_route.h>
> @@ -25,16 +26,20 @@
> #include <net/bluetooth/hci_core.h>
> #include <net/bluetooth/l2cap.h>
>
> -#include "6lowpan.h"
> -
> #include "../ieee802154/6lowpan.h" /* for the compression support */
>
> +#define VERSION "1.0"
> +
> +static struct dentry *lowpan_debugfs;
> +static struct l2cap_chan *chan_create(struct l2cap_conn *conn);
> +
> #define IFACE_NAME_TEMPLATE "bt%d"
> #define EUI64_ADDR_LEN 8
>
> struct skb_cb {
> struct in6_addr addr;
> - struct l2cap_conn *conn;
> + struct l2cap_chan *chan;
> + int status;
> };
> #define lowpan_cb(skb) ((struct skb_cb *)((skb)->cb))
>
> @@ -48,9 +53,15 @@ struct skb_cb {
> static LIST_HEAD(bt_6lowpan_devices);
> static DEFINE_RWLOCK(devices_lock);
>
> +/* If psm is set to 0 (default value), then 6lowpan is disabled.
> + * Other values are used to indicate a Protocol Service Multiplexer
> + * value for 6lowpan.
> + */
> +static u16 psm_6lowpan;
> +
> struct lowpan_peer {
> struct list_head list;
> - struct l2cap_conn *conn;
> + struct l2cap_chan *chan;
>
> /* peer addresses in various formats */
> unsigned char eui64_addr[EUI64_ADDR_LEN];
> @@ -101,13 +112,26 @@ static inline struct lowpan_peer *peer_lookup_ba(struct lowpan_dev *dev,
> ba, type);
>
> list_for_each_entry_safe(peer, tmp, &dev->peers, list) {
> - BT_DBG("addr %pMR type %d",
> - &peer->conn->hcon->dst, peer->conn->hcon->dst_type);
> + BT_DBG("dst addr %pMR dst type %d",
> + &peer->chan->dst, peer->chan->dst_type);
>
> - if (bacmp(&peer->conn->hcon->dst, ba))
> + if (bacmp(&peer->chan->dst, ba))
> continue;
>
> - if (type == peer->conn->hcon->dst_type)
> + if (type == peer->chan->dst_type)
> + return peer;
> + }
> +
> + return NULL;
> +}
> +
> +static inline struct lowpan_peer *peer_lookup_chan(struct lowpan_dev *dev,
> + struct l2cap_chan *chan)
> +{
> + struct lowpan_peer *peer, *tmp;
> +
> + list_for_each_entry_safe(peer, tmp, &dev->peers, list) {
> + if (peer->chan == chan)
> return peer;
> }
>
> @@ -120,7 +144,7 @@ static inline struct lowpan_peer *peer_lookup_conn(struct lowpan_dev *dev,
> struct lowpan_peer *peer, *tmp;
>
> list_for_each_entry_safe(peer, tmp, &dev->peers, list) {
> - if (peer->conn == conn)
> + if (peer->chan->conn == conn)
> return peer;
> }
>
> @@ -185,7 +209,7 @@ static int give_skb_to_upper(struct sk_buff *skb, struct net_device *dev)
> }
>
> static int process_data(struct sk_buff *skb, struct net_device *netdev,
> - struct l2cap_conn *conn)
> + struct l2cap_chan *chan)
> {
> const u8 *saddr, *daddr;
> u8 iphc0, iphc1;
> @@ -196,7 +220,7 @@ static int process_data(struct sk_buff *skb, struct net_device *netdev,
> dev = lowpan_dev(netdev);
>
> read_lock_irqsave(&devices_lock, flags);
> - peer = peer_lookup_conn(dev, conn);
> + peer = peer_lookup_chan(dev, chan);
> read_unlock_irqrestore(&devices_lock, flags);
> if (!peer)
> goto drop;
> @@ -225,7 +249,7 @@ drop:
> }
>
> static int recv_pkt(struct sk_buff *skb, struct net_device *dev,
> - struct l2cap_conn *conn)
> + struct l2cap_chan *chan)
> {
> struct sk_buff *local_skb;
> int ret;
> @@ -269,7 +293,7 @@ static int recv_pkt(struct sk_buff *skb, struct net_device *dev,
> if (!local_skb)
> goto drop;
>
> - ret = process_data(local_skb, dev, conn);
> + ret = process_data(local_skb, dev, chan);
> if (ret != NET_RX_SUCCESS)
> goto drop;
>
> @@ -286,140 +310,33 @@ static int recv_pkt(struct sk_buff *skb, struct net_device *dev,
> return NET_RX_SUCCESS;
>
> drop:
> + dev->stats.rx_dropped++;
> kfree_skb(skb);
> return NET_RX_DROP;
> }
>
> /* Packet from BT LE device */
> -int bt_6lowpan_recv(struct l2cap_conn *conn, struct sk_buff *skb)
> +static int bt_6lowpan_chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
> {
> struct lowpan_dev *dev;
> struct lowpan_peer *peer;
> int err;
>
> - peer = lookup_peer(conn);
> + peer = lookup_peer(chan->conn);
> if (!peer)
> return -ENOENT;
>
> - dev = lookup_dev(conn);
> + dev = lookup_dev(chan->conn);
> if (!dev || !dev->netdev)
> return -ENOENT;
>
> - err = recv_pkt(skb, dev->netdev, conn);
> + err = recv_pkt(skb, dev->netdev, chan);
> +
> BT_DBG("recv pkt %d", err);
>
> return err;
> }
>
> -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_ATOMIC);
> - if (!tmp)
> - return -ENOMEM;
> -
> - *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;
> -
> - /* FIXME: This mtu check should be not needed and atm is only used for
> - * testing purposes
> - */
> - if (conn->mtu > (L2CAP_LE_MIN_MTU + L2CAP_HDR_SIZE))
> - 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_ATOMIC);
> - if (!skb)
> - return ERR_PTR(-ENOMEM);
> -
> - 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;
> -
> - skb = create_pdu(conn, msg, len, priority, dev);
> - if (IS_ERR(skb))
> - return -EINVAL;
> -
> - 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 0;
> -}
> -
> static void get_dest_bdaddr(struct in6_addr *ip6_daddr,
> bdaddr_t *addr, u8 *addr_type)
> {
> @@ -466,7 +383,7 @@ static int header_create(struct sk_buff *skb, struct net_device *netdev,
> if (ipv6_addr_is_multicast(&hdr->daddr)) {
> memcpy(&lowpan_cb(skb)->addr, &hdr->daddr,
> sizeof(struct in6_addr));
> - lowpan_cb(skb)->conn = NULL;
> + lowpan_cb(skb)->chan = NULL;
> } else {
> unsigned long flags;
>
> @@ -490,7 +407,7 @@ static int header_create(struct sk_buff *skb, struct net_device *netdev,
>
> memcpy(&lowpan_cb(skb)->addr, &hdr->daddr,
> sizeof(struct in6_addr));
> - lowpan_cb(skb)->conn = peer->conn;
> + lowpan_cb(skb)->chan = peer->chan;
> }
>
> saddr = dev->netdev->dev_addr;
> @@ -499,14 +416,32 @@ static int header_create(struct sk_buff *skb, struct net_device *netdev,
> }
>
> /* Packet to BT LE device */
> -static int send_pkt(struct l2cap_conn *conn, const void *saddr,
> - const void *daddr, struct sk_buff *skb,
> +static int send_pkt(struct l2cap_chan *chan, struct sk_buff *skb,
> struct net_device *netdev)
> {
> - raw_dump_table(__func__, "raw skb data dump before fragmentation",
> - skb->data, skb->len);
> + int err;
> +
> + /* Remember the skb so that we can send EAGAIN to the caller if
> + * we run out of credits.
> + */
> + chan->data = skb;
> +
> + err = l2cap_chan_send(chan, skb->data, skb->len, 0, 0);
> + if (err > 0) {
> + netdev->stats.tx_bytes += err;
> + netdev->stats.tx_packets++;
> + err = 0;
> + } else if (err <= 0) {
> + if (err == 0)
> + err = lowpan_cb(skb)->status;
> +
> + if (err == -EAGAIN)
> + netdev->stats.tx_dropped++;
> + else if (err < 0)
> + netdev->stats.tx_errors++;
> + }
>
> - return conn_send(conn, skb->data, skb->len, 0, netdev);
> + return err;
> }
>
> static void send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev)
> @@ -529,8 +464,7 @@ static void send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev)
> list_for_each_entry_safe(pentry, ptmp, &dev->peers, list) {
> local_skb = skb_clone(skb, GFP_ATOMIC);
>
> - send_pkt(pentry->conn, netdev->dev_addr,
> - pentry->eui64_addr, local_skb, netdev);
> + send_pkt(pentry->chan, local_skb, netdev);
>
> kfree_skb(local_skb);
> }
> @@ -542,7 +476,6 @@ static void send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev)
> static netdev_tx_t bt_xmit(struct sk_buff *skb, struct net_device *netdev)
> {
> int err = 0;
> - unsigned char *eui64_addr;
> struct lowpan_dev *dev;
> struct lowpan_peer *peer;
> bdaddr_t addr;
> @@ -557,7 +490,6 @@ static netdev_tx_t bt_xmit(struct sk_buff *skb, struct net_device *netdev)
> unsigned long flags;
>
> get_dest_bdaddr(&lowpan_cb(skb)->addr, &addr, &addr_type);
> - eui64_addr = lowpan_cb(skb)->addr.s6_addr + 8;
> dev = lowpan_dev(netdev);
>
> read_lock_irqsave(&devices_lock, flags);
> @@ -567,9 +499,10 @@ static netdev_tx_t bt_xmit(struct sk_buff *skb, struct net_device *netdev)
> BT_DBG("xmit from %s to %pMR (%pI6c) peer %p", netdev->name,
> &addr, &lowpan_cb(skb)->addr, peer);
>
> - if (peer && peer->conn)
> - err = send_pkt(peer->conn, netdev->dev_addr,
> - eui64_addr, skb, netdev);
> + if (peer && peer->chan)
> + err = send_pkt(peer->chan, skb, netdev);
> + else
> + err = -ENOENT;
> }
> dev_kfree_skb(skb);
>
> @@ -661,26 +594,46 @@ static bool is_bt_6lowpan(struct hci_conn *hcon)
> if (hcon->type != LE_LINK)
> return false;
>
> - return test_bit(HCI_CONN_6LOWPAN, &hcon->flags);
> + if (!psm_6lowpan)
> + return false;
> +
> + return true;
> }
>
> -static int add_peer_conn(struct l2cap_conn *conn, struct lowpan_dev *dev)
> +static struct l2cap_chan *chan_open(struct l2cap_chan *pchan)
> +{
> + struct l2cap_chan *chan;
> +
> + chan = chan_create(pchan->conn);
> + if (!chan)
> + return NULL;
> +
> + chan->remote_mps = chan->omtu;
> + chan->mps = chan->omtu;
> +
> + chan->state = BT_CONNECTED;
> +
> + return chan;
> +}
> +
> +static struct l2cap_chan *add_peer_chan(struct l2cap_chan *chan,
> + struct lowpan_dev *dev)
> {
> struct lowpan_peer *peer;
> unsigned long flags;
>
> peer = kzalloc(sizeof(*peer), GFP_ATOMIC);
> if (!peer)
> - return -ENOMEM;
> + return NULL;
>
> - peer->conn = conn;
> + peer->chan = chan;
> memset(&peer->peer_addr, 0, sizeof(struct in6_addr));
>
> /* RFC 2464 ch. 5 */
> peer->peer_addr.s6_addr[0] = 0xFE;
> peer->peer_addr.s6_addr[1] = 0x80;
> - set_addr((u8 *)&peer->peer_addr.s6_addr + 8, conn->hcon->dst.b,
> - conn->hcon->dst_type);
> + set_addr((u8 *)&peer->peer_addr.s6_addr + 8, chan->dst.b,
> + chan->dst_type);
>
> memcpy(&peer->eui64_addr, (u8 *)&peer->peer_addr.s6_addr + 8,
> EUI64_ADDR_LEN);
> @@ -701,40 +654,24 @@ static int add_peer_conn(struct l2cap_conn *conn, struct lowpan_dev *dev)
> INIT_DELAYED_WORK(&dev->notify_peers, do_notify_peers);
> schedule_delayed_work(&dev->notify_peers, msecs_to_jiffies(100));
>
> - return 0;
> + return peer->chan;
> }
>
> -/* 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)
> +static int setup_netdev(struct l2cap_chan *chan, struct lowpan_dev **dev)
> {
> - struct lowpan_peer *peer = NULL;
> - struct lowpan_dev *dev;
> struct net_device *netdev;
> int err = 0;
> unsigned long flags;
>
> - if (!is_bt_6lowpan(conn->hcon))
> - return 0;
> -
> - peer = lookup_peer(conn);
> - if (peer)
> - return -EEXIST;
> -
> - dev = lookup_dev(conn);
> - if (dev)
> - return add_peer_conn(conn, dev);
> -
> - netdev = alloc_netdev(sizeof(*dev), IFACE_NAME_TEMPLATE, netdev_setup);
> + netdev = alloc_netdev(sizeof(struct lowpan_dev), IFACE_NAME_TEMPLATE,
> + netdev_setup);
> if (!netdev)
> return -ENOMEM;
>
> - set_dev_addr(netdev, &conn->hcon->src, conn->hcon->src_type);
> + set_dev_addr(netdev, &chan->src, chan->src_type);
>
> netdev->netdev_ops = &netdev_ops;
> - SET_NETDEV_DEV(netdev, &conn->hcon->dev);
> + SET_NETDEV_DEV(netdev, &chan->conn->hcon->dev);
> SET_NETDEV_DEVTYPE(netdev, &bt_type);
>
> err = register_netdev(netdev);
> @@ -744,28 +681,54 @@ int bt_6lowpan_add_conn(struct l2cap_conn *conn)
> goto out;
> }
>
> - BT_DBG("ifindex %d peer bdaddr %pMR my addr %pMR",
> - netdev->ifindex, &conn->hcon->dst, &conn->hcon->src);
> + BT_DBG("ifindex %d peer bdaddr %pMR type %d my addr %pMR type %d",
> + netdev->ifindex, &chan->dst, chan->dst_type,
> + &chan->src, chan->src_type);
> set_bit(__LINK_STATE_PRESENT, &netdev->state);
>
> - dev = netdev_priv(netdev);
> - dev->netdev = netdev;
> - dev->hdev = conn->hcon->hdev;
> - INIT_LIST_HEAD(&dev->peers);
> + *dev = netdev_priv(netdev);
> + (*dev)->netdev = netdev;
> + (*dev)->hdev = chan->conn->hcon->hdev;
> + INIT_LIST_HEAD(&(*dev)->peers);
>
> write_lock_irqsave(&devices_lock, flags);
> - INIT_LIST_HEAD(&dev->list);
> - list_add(&dev->list, &bt_6lowpan_devices);
> + INIT_LIST_HEAD(&(*dev)->list);
> + list_add(&(*dev)->list, &bt_6lowpan_devices);
> write_unlock_irqrestore(&devices_lock, flags);
>
> - ifup(netdev);
> -
> - return add_peer_conn(conn, dev);
> + return 0;
>
> out:
> return err;
> }
>
> +static inline void bt_6lowpan_chan_ready_cb(struct l2cap_chan *chan)
> +{
> + struct lowpan_dev *dev;
> +
> + dev = lookup_dev(chan->conn);
> +
> + BT_DBG("chan %p conn %p dev %p", chan, chan->conn, dev);
> +
> + if (!dev) {
> + if (setup_netdev(chan, &dev) < 0) {
> + l2cap_chan_del(chan, -ENOENT);
> + return;
> + }
> + }
> +
> + add_peer_chan(chan, dev);
> + ifup(dev->netdev);
> +}
> +
> +static inline
> +struct l2cap_chan *bt_6lowpan_chan_new_connection_cb(struct l2cap_chan *chan)
> +{
> + BT_DBG("chan %p", chan);
> +
> + return chan_open(chan);
> +}
> +
> static void delete_netdev(struct work_struct *work)
> {
> struct lowpan_dev *entry = container_of(work, struct lowpan_dev,
> @@ -776,26 +739,39 @@ static void delete_netdev(struct work_struct *work)
> /* The entry pointer is deleted in device_event() */
> }
>
> -int bt_6lowpan_del_conn(struct l2cap_conn *conn)
> +static void bt_6lowpan_chan_close_cb(struct l2cap_chan *chan)
> {
> struct lowpan_dev *entry, *tmp;
> struct lowpan_dev *dev = NULL;
> struct lowpan_peer *peer;
> int err = -ENOENT;
> unsigned long flags;
> - bool last = false;
> + bool last = false, removed = true;
> +
> + BT_DBG("chan %p conn %p", chan, chan->conn);
>
> - if (!conn || !is_bt_6lowpan(conn->hcon))
> - return 0;
> + if (chan->conn && chan->conn->hcon) {
> + if (!is_bt_6lowpan(chan->conn->hcon))
> + return;
> +
> + /*
> + * If conn is set, then the netdev is also there and we should
> + * not remove it.
> + */
> + removed = false;
> + }
>
> write_lock_irqsave(&devices_lock, flags);
>
> list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) {
> dev = lowpan_dev(entry->netdev);
> - peer = peer_lookup_conn(dev, conn);
> + peer = peer_lookup_chan(dev, chan);
> if (peer) {
> last = peer_del(dev, peer);
> err = 0;
> + BT_DBG("dev %p removing %speer %p", dev,
> + last ? "last " : "1 ", peer);
> + kfree(peer);
> break;
> }
> }
> @@ -805,18 +781,407 @@ int bt_6lowpan_del_conn(struct l2cap_conn *conn)
>
> cancel_delayed_work_sync(&dev->notify_peers);
>
> - /* bt_6lowpan_del_conn() 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);
> + if (!removed) {
> + INIT_WORK(&entry->delete_netdev, delete_netdev);
> + schedule_work(&entry->delete_netdev);
> + }
> } else {
> write_unlock_irqrestore(&devices_lock, flags);
> }
>
> + return;
> +}
> +
> +static void bt_6lowpan_chan_state_change_cb(struct l2cap_chan *chan, int state,
> + int err)
> +{
> + BT_DBG("chan %p conn %p", chan, chan->conn);
> +}
> +
> +static struct sk_buff *bt_6lowpan_chan_alloc_skb_cb(struct l2cap_chan *chan,
> + unsigned long len, int nb)
> +{
> + return bt_skb_alloc(len, GFP_ATOMIC);
Does this have to be GFP_ATOMIC?
> +}
> +
> +static void bt_6lowpan_chan_suspend_cb(struct l2cap_chan *chan)
> +{
> + struct sk_buff *skb = chan->data;
> +
> + BT_DBG("chan %p conn %p skb %p", chan, chan->conn, skb);
> +
> + lowpan_cb(skb)->status = -EAGAIN;
> +}
> +
> +static void bt_6lowpan_chan_resume_cb(struct l2cap_chan *chan)
> +{
> + struct sk_buff *skb = chan->data;
> +
> + BT_DBG("chan %p conn %p skb %p", chan, chan->conn, skb);
> +
> + lowpan_cb(skb)->status = 0;
> +}
> +
> +static long bt_l2cap_chan_get_sndtimeo(struct l2cap_chan *chan)
> +{
> + return msecs_to_jiffies(1000);
> +}
> +
> +static void bt_6lowpan_chan_teardown_cb(struct l2cap_chan *chan, int err)
> +{
> + BT_DBG("chan %p conn %p err %d", chan, chan->conn, err);
> +}
> +
> +static struct l2cap_ops bt_6lowpan_chan_ops = {
> + .name = "L2CAP 6LoWPAN channel",
> + .new_connection = bt_6lowpan_chan_new_connection_cb,
> + .recv = bt_6lowpan_chan_recv_cb,
> + .teardown = bt_6lowpan_chan_teardown_cb,
> + .close = bt_6lowpan_chan_close_cb,
> + .state_change = bt_6lowpan_chan_state_change_cb,
> + .ready = bt_6lowpan_chan_ready_cb,
> + .resume = bt_6lowpan_chan_resume_cb,
> + .suspend = bt_6lowpan_chan_suspend_cb,
> + .get_sndtimeo = bt_l2cap_chan_get_sndtimeo,
> + .alloc_skb = bt_6lowpan_chan_alloc_skb_cb,
> +
> + .defer = l2cap_chan_no_defer,
> + .set_shutdown = l2cap_chan_no_set_shutdown,
> +};
> +
> +static struct l2cap_chan *chan_create(struct l2cap_conn *conn)
> +{
> + struct l2cap_chan *chan;
> +
> + chan = l2cap_chan_create();
> + if (!chan)
> + return NULL;
> +
> + l2cap_chan_set_defaults(chan);
> +
> + chan->chan_type = L2CAP_CHAN_CONN_ORIENTED;
> + chan->mode = L2CAP_MODE_LE_FLOWCTL;
> + chan->omtu = 65535;
> + chan->imtu = chan->omtu;
> + chan->ops = &bt_6lowpan_chan_ops;
> +
> + return chan;
> +}
> +
> +static struct l2cap_chan *bt_6lowpan_channel_create(struct l2cap_conn *conn)
> +{
> + struct lowpan_peer *peer;
> +
> + if (!is_bt_6lowpan(conn->hcon))
> + return NULL;
> +
> + BT_DBG("conn %p", conn);
> +
> + peer = lookup_peer(conn);
> + if (peer) {
> + BT_DBG("6LoWPAN connection and channel already exists");
> + return NULL;
> + }
> +
> + return chan_create(conn);
> +}
> +
> +static inline __u8 bdaddr_type(__u8 link_type, __u8 type)
> +{
> + if (link_type == LE_LINK) {
> + if (type == ADDR_LE_DEV_PUBLIC)
> + return BDADDR_LE_PUBLIC;
> + else
> + return BDADDR_LE_RANDOM;
> + }
> +
> + return BDADDR_BREDR;
> +}
This is a bit weird since we only have 6loWPAN support defined for LE links. We might need to just fail early one and then just check the address type.
> +
> +static int bt_6lowpan_connect(struct l2cap_conn *conn, u8 dst_type)
> +{
> + struct hci_conn *hcon = conn->hcon;
> + struct l2cap_chan *pchan;
> + int err;
> +
> + BT_DBG("conn %p dst %pMR type %d user %d", conn, &hcon->dst,
> + hcon->dst_type, dst_type);
> +
> + if (hci_blacklist_lookup(hcon->hdev, &hcon->dst, hcon->dst_type))
> + return -EACCES;
Is this handling correct here? I think the hci_blacklist part should be handled inside hci_core and not in the 6loWPAN module. My thinking is that it should be handled before it reaches L2CAP connect.
> +
> + pchan = bt_6lowpan_channel_create(conn);
> + if (!pchan)
> + return -EINVAL;
> +
> + err = l2cap_chan_connect(pchan, cpu_to_le16(psm_6lowpan), 0,
> + &hcon->dst, dst_type);
> +
> + BT_DBG("chan %p err %d", pchan, err);
> +
> return err;
> }
>
> +static int bt_6lowpan_disconnect(struct l2cap_conn *conn, u8 dst_type)
> +{
> + struct lowpan_peer *peer;
> +
> + BT_DBG("conn %p dst type %d", conn, dst_type);
> +
> + peer = lookup_peer(conn);
> + if (!peer)
> + return -ENOENT;
> +
> + l2cap_chan_close(peer->chan, ENOENT);
> +
> + return 0;
> +}
> +
> +static int bt_6lowpan_listen(struct l2cap_conn *conn)
> +{
> + struct hci_conn *hcon = conn->hcon;
> + struct l2cap_chan *pchan;
> + int err;
> +
> + if (!conn)
> + return -ENOENT;
> +
> + pchan = bt_6lowpan_channel_create(conn);
> + if (!pchan)
> + return -ENOENT;
> +
> + pchan->state = BT_LISTEN;
> + pchan->src_type = bdaddr_type(hcon->type, hcon->src_type);
> +
> + BT_DBG("psm 0x%04x chan %p src type %d", psm_6lowpan, pchan,
> + pchan->src_type);
> +
> + err = l2cap_add_psm(pchan, &hcon->src,
> + cpu_to_le16(psm_6lowpan));
> + if (err) {
> + BT_ERR("psm cannot be added err %d", err);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int get_l2cap_conn(struct hci_dev *hdev, char *buf,
> + bdaddr_t *addr, u8 *addr_type,
> + struct l2cap_conn **conn)
> +{
> + struct hci_conn *hcon;
> + int n;
> +
> + n = sscanf(buf, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx %hhu",
> + &addr->b[5], &addr->b[4], &addr->b[3],
> + &addr->b[2], &addr->b[1], &addr->b[0],
> + addr_type);
> +
> + if (n < 7)
> + return -EINVAL;
> +
> + hci_dev_lock(hdev);
> + hcon = hci_conn_hash_lookup_ba(hdev, LE_LINK, addr);
> + hci_dev_unlock(hdev);
> +
> + if (!hcon)
> + return -ENOENT;
> +
> + *conn = (struct l2cap_conn *)hcon->l2cap_data;
> +
> + BT_DBG("conn %p dst %pMR type %d", *conn, &hcon->dst, hcon->dst_type);
> +
> + return 0;
> +}
> +
> +static void disconnect_all_peers(struct hci_dev *hdev)
> +{
> + struct lowpan_dev *entry, *tmp_dev;
> + struct lowpan_peer *peer, *tmp_peer, *new_peer;
> + struct list_head peers;
> + unsigned long flags;
> +
> + INIT_LIST_HEAD(&peers);
> +
> + /* We make a separate list of peers as the close_cb() will
> + * modify the device peers list so it is better not to mess
> + * with the same list at the same time.
> + */
> +
> + hci_dev_lock(hdev);
> +
> + read_lock_irqsave(&devices_lock, flags);
> +
> + list_for_each_entry_safe(entry, tmp_dev, &bt_6lowpan_devices, list) {
> + list_for_each_entry_safe(peer, tmp_peer, &entry->peers, list) {
> + new_peer = kmalloc(sizeof(*new_peer), GFP_ATOMIC);
> + if (!new_peer)
> + break;
> +
> + new_peer->chan = peer->chan;
> + INIT_LIST_HEAD(&new_peer->list);
> +
> + list_add(&new_peer->list, &peers);
> + }
> + }
> +
> + read_unlock_irqrestore(&devices_lock, flags);
> +
> + hci_dev_unlock(hdev);
> +
> + list_for_each_entry_safe(peer, tmp_peer, &peers, list) {
> + l2cap_chan_close(peer->chan, ENOENT);
> + kfree(peer);
> + }
> +}
> +
> +static ssize_t lowpan_write(struct file *fp, const char __user *user_buffer,
> + size_t count, loff_t *position)
> +{
> + struct seq_file *f = fp->private_data;
> + struct hci_dev *hdev = f->private;
> + char buf[32];
> + size_t buf_size = min(count, sizeof(buf) - 1);
> + int ret;
> + bdaddr_t addr;
> + u8 addr_type;
> + struct l2cap_conn *conn;
> +
> + if (copy_from_user(buf, user_buffer, buf_size))
> + return -EFAULT;
> +
> + buf[buf_size] = '\0';
> +
> + if (memcmp(buf, "psm ", 4) == 0) {
> + unsigned long value;
> + u16 psm;
> +
> + ret = kstrtoul(&buf[4], 0, &value);
> + if (ret < 0)
> + return ret;
> +
> + psm = value;
> + if (psm == 0 || psm_6lowpan != psm)
> + /* Disconnect existing connections if 6lowpan is
> + * disabled (psm = 0), or if psm changes.
> + */
> + disconnect_all_peers(hdev);
> +
> + psm_6lowpan = psm;
> +
> + return count;
> + }
I get the feeling that we should have one 6lowpan_psm debugfs entry and another one 6lowpan_control.
> +
> + if (memcmp(buf, "connect ", 8) == 0) {
> +
> + ret = get_l2cap_conn(hdev, &buf[8], &addr, &addr_type, &conn);
> + if (ret < 0)
> + return ret;
> +
> + ret = bt_6lowpan_connect(conn, addr_type);
> + if (ret < 0)
> + return ret;
> +
> + return count;
> + }
> +
> + if (memcmp(buf, "disconnect ", 11) == 0) {
> +
> + ret = get_l2cap_conn(hdev, &buf[11], &addr, &addr_type, &conn);
> + if (ret < 0)
> + return ret;
> +
> + ret = bt_6lowpan_disconnect(conn, addr_type);
> + if (ret < 0)
> + return ret;
> +
> + return count;
> + }
> +
> + return count;
> +}
> +
> +static int lowpan_show(struct seq_file *f, void *ptr)
> +{
> + struct hci_dev *hdev = f->private;
> + struct lowpan_dev *entry, *tmp_dev;
> + struct lowpan_peer *peer, *tmp_peer;
> + unsigned long flags;
> +
> + seq_printf(f, "psm %u\n", psm_6lowpan);
This one makes it even more clearer that the 6lowpan_psm should be its own debugfs file.
> +
> + hci_dev_lock(hdev);
> + read_lock_irqsave(&devices_lock, flags);
> +
> + list_for_each_entry_safe(entry, tmp_dev, &bt_6lowpan_devices, list) {
> + list_for_each_entry_safe(peer, tmp_peer, &entry->peers, list)
> + seq_printf(f, "%pMR (type %u)\n",
> + &peer->chan->dst, peer->chan->dst_type);
> + }
> +
> + read_unlock_irqrestore(&devices_lock, flags);
> + hci_dev_unlock(hdev);
> +
> + return 0;
> +}
> +
> +static int lowpan_open(struct inode *inode, struct file *file)
> +{
> + return single_open(file, lowpan_show, inode->i_private);
> +}
> +
> +static const struct file_operations lowpan_debugfs_fops = {
> + .open = lowpan_open,
> + .read = seq_read,
> + .write = lowpan_write,
> + .llseek = seq_lseek,
> + .release = single_release,
> +};
> +
> +struct lowpan_check {
> + struct hci_dev *hdev;
> + struct work_struct setup_6lowpan;
> +};
> +
> +static void setup_6lowpan(struct work_struct *work)
> +{
> + struct lowpan_check *check = container_of(work, struct lowpan_check,
> + setup_6lowpan);
> + struct hci_dev *hdev = check->hdev;
> +
> + kfree(check);
> +
> + lowpan_debugfs = debugfs_create_file("6lowpan", 0644, hdev->debugfs,
> + hdev, &lowpan_debugfs_fops);
You can not use hdev->debugfs here. That is per HCI device. The 6loWPAN debugfs entries should be just global for all devices. Use bt_debugfs here.
Not to mention that you create it twice if you are attaching two controllers to the same system. This will just break horrible.
> +}
> +
> +static void lowpan_connect_hcon(struct hci_conn *hcon)
> +{
> + struct l2cap_conn *conn = hcon->l2cap_data;
> +
> + if (!conn || !psm_6lowpan)
> + return;
> +
> + bt_6lowpan_listen(conn);
> +}
> +
> +static void lowpan_create_hci(struct hci_dev *hdev)
> +{
> + struct lowpan_check *check;
> +
> + check = kmalloc(sizeof(struct lowpan_check), GFP_ATOMIC);
> + if (!check)
> + return;
> +
> + BT_DBG("hdev %p", hdev);
> +
> + check->hdev = hdev;
> +
> + INIT_WORK(&check->setup_6lowpan, setup_6lowpan);
> + schedule_work(&check->setup_6lowpan);
> +}
> +
> static int device_event(struct notifier_block *unused,
> unsigned long event, void *ptr)
> {
> @@ -849,12 +1214,24 @@ static struct notifier_block bt_6lowpan_dev_notifier = {
> .notifier_call = device_event,
> };
>
> +static struct hci_cb lowpan_cb = {
> + .name = "6LoWPAN",
> + .create_cfm = lowpan_create_hci,
> + .connect_cfm = lowpan_connect_hcon,
> +};
> +
> int bt_6lowpan_init(void)
> {
> + hci_register_cb(&lowpan_cb);
> +
Yep. You need to create the 6loWPAN debugfs entries here. Otherwise you can not configure it until the controller is there.
> return register_netdevice_notifier(&bt_6lowpan_dev_notifier);
> }
>
> void bt_6lowpan_cleanup(void)
> {
> + debugfs_remove(lowpan_debugfs);
If you ever attach/remove/attach a controller or attach a second controller, this is your memory leak of the debugfs dentry right here.
> +
> + hci_unregister_cb(&lowpan_cb);
> +
> unregister_netdevice_notifier(&bt_6lowpan_dev_notifier);
> }
Regards
Marcel
Hi Jukka,
> Signed-off-by: Jukka Rissanen <[email protected]>
> ---
> net/bluetooth/l2cap_core.c | 6 ++++++
> 1 file changed, 6 insertions(+)
>
> diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
> index 60433c4..3a7f560 100644
> --- a/net/bluetooth/l2cap_core.c
> +++ b/net/bluetooth/l2cap_core.c
> @@ -471,8 +471,14 @@ void l2cap_chan_set_defaults(struct l2cap_chan *chan)
> chan->max_tx = L2CAP_DEFAULT_MAX_TX;
> chan->tx_win = L2CAP_DEFAULT_TX_WINDOW;
> chan->tx_win_max = L2CAP_DEFAULT_TX_WINDOW;
> + chan->remote_max_tx = chan->max_tx;
> + chan->remote_tx_win = chan->tx_win;
> chan->ack_win = L2CAP_DEFAULT_TX_WINDOW;
> chan->sec_level = BT_SECURITY_LOW;
> + chan->flush_to = L2CAP_DEFAULT_FLUSH_TO;
> + chan->retrans_timeout = L2CAP_DEFAULT_RETRANS_TO;
> + chan->monitor_timeout = L2CAP_DEFAULT_MONITOR_TO;
> + chan->conf_state = 0;
this one extremely bad without a detailed commit message explaining why the change is made.
Regards
Marcel
Hi Jukka,
> Signed-off-by: Jukka Rissanen <[email protected]>
> ---
> include/net/bluetooth/hci_core.h | 26 ++++++++++++++++++++++++++
> net/bluetooth/hci_event.c | 3 +++
> 2 files changed, 29 insertions(+)
here as well. I need a commit message body that explains the change.
Regards
Marcel
Hi Jukka,
> The l2cap_chan_send() is changed to use kernel memory directly,
> so this function must read the user buffer before sending the
> message.
>
> The change is done as the 6LoWPAN also uses l2cap_chan_send()
> and in order to minimize the amount of code changes, we must
> copy the user buffer in sock handling code.
>
> Signed-off-by: Jukka Rissanen <[email protected]>
> ---
> include/net/bluetooth/l2cap.h | 4 +--
> net/bluetooth/a2mp.c | 12 +------
> net/bluetooth/l2cap_core.c | 76 ++++++++++++++++++++++++-------------------
> net/bluetooth/l2cap_sock.c | 14 +++++++-
> 4 files changed, 58 insertions(+), 48 deletions(-)
>
> diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
> index 4abdcb2..3980b81 100644
> --- a/include/net/bluetooth/l2cap.h
> +++ b/include/net/bluetooth/l2cap.h
> @@ -872,8 +872,8 @@ struct l2cap_chan *l2cap_chan_create(void);
> void l2cap_chan_close(struct l2cap_chan *chan, int reason);
> int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
> bdaddr_t *dst, u8 dst_type);
> -int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
> - u32 priority);
> +int l2cap_chan_send(struct l2cap_chan *chan, unsigned char *msg, size_t len,
> + u32 priority, unsigned int flags);
use buf instead of msg here. The only time we really use msg is when it is actually a msghdr struct. Might want to consider to make it void *buf.
> void l2cap_chan_busy(struct l2cap_chan *chan, int busy);
> int l2cap_chan_check_security(struct l2cap_chan *chan);
> void l2cap_chan_set_defaults(struct l2cap_chan *chan);
> diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
> index 9514cc9..9efcda8 100644
> --- a/net/bluetooth/a2mp.c
> +++ b/net/bluetooth/a2mp.c
> @@ -48,22 +48,12 @@ 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;
> u16 total_len = len + sizeof(*cmd);
> - struct kvec iv;
> - struct msghdr msg;
>
> cmd = __a2mp_build(code, ident, len, data);
> if (!cmd)
> return;
>
> - iv.iov_base = cmd;
> - iv.iov_len = total_len;
> -
> - memset(&msg, 0, sizeof(msg));
> -
> - msg.msg_iov = (struct iovec *) &iv;
> - msg.msg_iovlen = 1;
> -
> - l2cap_chan_send(chan, &msg, total_len, 0);
> + l2cap_chan_send(chan, (unsigned char *)cmd, total_len, 0, 0);
Why are we casting here? I do not like these casts at all.
> kfree(cmd);
> }
> diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
> index a1e5bb7..60433c4 100644
> --- a/net/bluetooth/l2cap_core.c
> +++ b/net/bluetooth/l2cap_core.c
> @@ -2098,19 +2098,20 @@ static void l2cap_send_ack(struct l2cap_chan *chan)
> }
> }
>
> -static inline int l2cap_skbuff_fromiovec(struct l2cap_chan *chan,
> - struct msghdr *msg, int len,
> - int count, struct sk_buff *skb)
> +static inline int l2cap_skbuff(struct l2cap_chan *chan,
> + unsigned char *msg, int len,
> + unsigned int flags, int count,
> + struct sk_buff *skb)
Same here. void *buf seems a bit better. Also the function name might need a bit clearer name. It seems a bit too generic right now.
> {
> struct l2cap_conn *conn = chan->conn;
> struct sk_buff **frag;
> int sent = 0;
>
> - if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count))
> - return -EFAULT;
> + memcpy(skb_put(skb, count), msg, count);
>
> sent += count;
> len -= count;
> + msg += count;
>
> /* Continuation fragments (no L2CAP header) */
> frag = &skb_shinfo(skb)->frag_list;
> @@ -2120,19 +2121,19 @@ static inline int l2cap_skbuff_fromiovec(struct l2cap_chan *chan,
> count = min_t(unsigned int, conn->mtu, len);
>
> tmp = chan->ops->alloc_skb(chan, count,
> - msg->msg_flags & MSG_DONTWAIT);
> + flags & MSG_DONTWAIT);
> if (IS_ERR(tmp))
> return PTR_ERR(tmp);
>
> *frag = tmp;
>
> - if (memcpy_fromiovec(skb_put(*frag, count), msg->msg_iov, count))
> - return -EFAULT;
> + memcpy(skb_put(*frag, count), msg, count);
>
> (*frag)->priority = skb->priority;
>
> sent += count;
> len -= count;
> + msg += count;
>
> skb->len += (*frag)->len;
> skb->data_len += (*frag)->len;
> @@ -2144,8 +2145,8 @@ static inline int l2cap_skbuff_fromiovec(struct l2cap_chan *chan,
> }
>
> static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan,
> - struct msghdr *msg, size_t len,
> - u32 priority)
> + unsigned char *msg, size_t len,
> + u32 priority, unsigned int flags)
> {
> struct l2cap_conn *conn = chan->conn;
> struct sk_buff *skb;
> @@ -2158,7 +2159,7 @@ static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan,
> count = min_t(unsigned int, (conn->mtu - hlen), len);
>
> skb = chan->ops->alloc_skb(chan, count + hlen,
> - msg->msg_flags & MSG_DONTWAIT);
> + flags & MSG_DONTWAIT);
> if (IS_ERR(skb))
> return skb;
>
> @@ -2170,7 +2171,7 @@ static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan,
> lh->len = cpu_to_le16(len + L2CAP_PSMLEN_SIZE);
> put_unaligned(chan->psm, (__le16 *) skb_put(skb, L2CAP_PSMLEN_SIZE));
>
> - err = l2cap_skbuff_fromiovec(chan, msg, len, count, skb);
> + err = l2cap_skbuff(chan, msg, len, flags, count, skb);
> if (unlikely(err < 0)) {
> kfree_skb(skb);
> return ERR_PTR(err);
> @@ -2179,8 +2180,8 @@ static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan,
> }
>
> static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan,
> - struct msghdr *msg, size_t len,
> - u32 priority)
> + unsigned char *msg, size_t len,
> + u32 priority, unsigned int flags)
Same here as well. void *buf.
> {
> struct l2cap_conn *conn = chan->conn;
> struct sk_buff *skb;
> @@ -2192,7 +2193,7 @@ static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan,
> count = min_t(unsigned int, (conn->mtu - L2CAP_HDR_SIZE), len);
>
> skb = chan->ops->alloc_skb(chan, count + L2CAP_HDR_SIZE,
> - msg->msg_flags & MSG_DONTWAIT);
> + flags & MSG_DONTWAIT);
> if (IS_ERR(skb))
> return skb;
>
> @@ -2203,7 +2204,7 @@ static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan,
> lh->cid = cpu_to_le16(chan->dcid);
> lh->len = cpu_to_le16(len);
>
> - err = l2cap_skbuff_fromiovec(chan, msg, len, count, skb);
> + err = l2cap_skbuff(chan, msg, len, flags, count, skb);
> if (unlikely(err < 0)) {
> kfree_skb(skb);
> return ERR_PTR(err);
> @@ -2212,8 +2213,8 @@ static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan,
> }
>
> static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan,
> - struct msghdr *msg, size_t len,
> - u16 sdulen)
> + unsigned char *msg, size_t len,
> + u16 sdulen, unsigned int flags)
And here. And so on ;)
> {
> struct l2cap_conn *conn = chan->conn;
> struct sk_buff *skb;
> @@ -2236,7 +2237,7 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan,
> count = min_t(unsigned int, (conn->mtu - hlen), len);
>
> skb = chan->ops->alloc_skb(chan, count + hlen,
> - msg->msg_flags & MSG_DONTWAIT);
> + flags & MSG_DONTWAIT);
> if (IS_ERR(skb))
> return skb;
>
> @@ -2254,7 +2255,7 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan,
> if (sdulen)
> put_unaligned_le16(sdulen, skb_put(skb, L2CAP_SDULEN_SIZE));
>
> - err = l2cap_skbuff_fromiovec(chan, msg, len, count, skb);
> + err = l2cap_skbuff(chan, msg, len, flags, count, skb);
> if (unlikely(err < 0)) {
> kfree_skb(skb);
> return ERR_PTR(err);
> @@ -2267,7 +2268,8 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan,
>
> static int l2cap_segment_sdu(struct l2cap_chan *chan,
> struct sk_buff_head *seg_queue,
> - struct msghdr *msg, size_t len)
> + unsigned char *msg, size_t len,
> + unsigned int flags)
> {
> struct sk_buff *skb;
> u16 sdu_len;
> @@ -2308,7 +2310,8 @@ static int l2cap_segment_sdu(struct l2cap_chan *chan,
> }
>
> while (len > 0) {
> - skb = l2cap_create_iframe_pdu(chan, msg, pdu_len, sdu_len);
> + skb = l2cap_create_iframe_pdu(chan, msg, pdu_len, sdu_len,
> + flags);
>
> if (IS_ERR(skb)) {
> __skb_queue_purge(seg_queue);
> @@ -2336,8 +2339,9 @@ static int l2cap_segment_sdu(struct l2cap_chan *chan,
> }
>
> static struct sk_buff *l2cap_create_le_flowctl_pdu(struct l2cap_chan *chan,
> - struct msghdr *msg,
> - size_t len, u16 sdulen)
> + unsigned char *msg,
> + size_t len, u16 sdulen,
> + unsigned int flags)
> {
> struct l2cap_conn *conn = chan->conn;
> struct sk_buff *skb;
> @@ -2357,7 +2361,7 @@ static struct sk_buff *l2cap_create_le_flowctl_pdu(struct l2cap_chan *chan,
> count = min_t(unsigned int, (conn->mtu - hlen), len);
>
> skb = chan->ops->alloc_skb(chan, count + hlen,
> - msg->msg_flags & MSG_DONTWAIT);
> + flags & MSG_DONTWAIT);
> if (IS_ERR(skb))
> return skb;
>
> @@ -2369,7 +2373,7 @@ static struct sk_buff *l2cap_create_le_flowctl_pdu(struct l2cap_chan *chan,
> if (sdulen)
> put_unaligned_le16(sdulen, skb_put(skb, L2CAP_SDULEN_SIZE));
>
> - err = l2cap_skbuff_fromiovec(chan, msg, len, count, skb);
> + err = l2cap_skbuff(chan, msg, len, flags, count, skb);
> if (unlikely(err < 0)) {
> kfree_skb(skb);
> return ERR_PTR(err);
> @@ -2380,7 +2384,8 @@ static struct sk_buff *l2cap_create_le_flowctl_pdu(struct l2cap_chan *chan,
>
> static int l2cap_segment_le_sdu(struct l2cap_chan *chan,
> struct sk_buff_head *seg_queue,
> - struct msghdr *msg, size_t len)
> + unsigned char *msg, size_t len,
> + unsigned int flags)
> {
> struct sk_buff *skb;
> size_t pdu_len;
> @@ -2399,7 +2404,8 @@ static int l2cap_segment_le_sdu(struct l2cap_chan *chan,
> if (len <= pdu_len)
> pdu_len = len;
>
> - skb = l2cap_create_le_flowctl_pdu(chan, msg, pdu_len, sdu_len);
> + skb = l2cap_create_le_flowctl_pdu(chan, msg, pdu_len, sdu_len,
> + flags);
> if (IS_ERR(skb)) {
> __skb_queue_purge(seg_queue);
> return PTR_ERR(skb);
> @@ -2408,6 +2414,7 @@ static int l2cap_segment_le_sdu(struct l2cap_chan *chan,
> __skb_queue_tail(seg_queue, skb);
>
> len -= pdu_len;
> + msg += pdu_len;
>
> if (sdu_len) {
> sdu_len = 0;
> @@ -2418,8 +2425,8 @@ static int l2cap_segment_le_sdu(struct l2cap_chan *chan,
> return 0;
> }
>
> -int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
> - u32 priority)
> +int l2cap_chan_send(struct l2cap_chan *chan, unsigned char *msg, size_t len,
> + u32 priority, unsigned int flags)
> {
> struct sk_buff *skb;
> int err;
> @@ -2430,7 +2437,8 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
>
> /* Connectionless channel */
> if (chan->chan_type == L2CAP_CHAN_CONN_LESS) {
> - skb = l2cap_create_connless_pdu(chan, msg, len, priority);
> + skb = l2cap_create_connless_pdu(chan, msg, len, priority,
> + flags);
> if (IS_ERR(skb))
> return PTR_ERR(skb);
>
> @@ -2457,7 +2465,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
>
> __skb_queue_head_init(&seg_queue);
>
> - err = l2cap_segment_le_sdu(chan, &seg_queue, msg, len);
> + err = l2cap_segment_le_sdu(chan, &seg_queue, msg, len, flags);
>
> if (chan->state != BT_CONNECTED) {
> __skb_queue_purge(&seg_queue);
> @@ -2487,7 +2495,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
> return -EMSGSIZE;
>
> /* Create a basic PDU */
> - skb = l2cap_create_basic_pdu(chan, msg, len, priority);
> + skb = l2cap_create_basic_pdu(chan, msg, len, priority, flags);
> if (IS_ERR(skb))
> return PTR_ERR(skb);
>
> @@ -2517,7 +2525,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
> * since it's possible to block while waiting for memory
> * allocation.
> */
> - err = l2cap_segment_sdu(chan, &seg_queue, msg, len);
> + err = l2cap_segment_sdu(chan, &seg_queue, msg, len, flags);
>
> /* The channel could have been closed while segmenting,
> * check that it is still connected.
> diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
> index f59e00c..7e7b28a 100644
> --- a/net/bluetooth/l2cap_sock.c
> +++ b/net/bluetooth/l2cap_sock.c
> @@ -948,6 +948,7 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
> {
> struct sock *sk = sock->sk;
> struct l2cap_chan *chan = l2cap_pi(sk)->chan;
> + unsigned char *buf;
> int err;
>
> BT_DBG("sock %p, sk %p", sock, sk);
> @@ -968,10 +969,21 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
> if (err)
> return err;
>
> + buf = kmalloc(len, GFP_KERNEL);
> + if (!buf)
> + return -ENOMEM;
> +
> + if (memcpy_fromiovec(buf, msg->msg_iov, len)) {
> + err = -EFAULT;
> + goto done;
> + }
> +
> l2cap_chan_lock(chan);
> - err = l2cap_chan_send(chan, msg, len, sk->sk_priority);
> + err = l2cap_chan_send(chan, buf, len, sk->sk_priority, msg->msg_flags);
> l2cap_chan_unlock(chan);
>
> +done:
> + kfree(buf);
> return err;
> }
We don?t have to use chan->ops->alloc_skb here? Has this become obsolete now?
Regards
Marcel
Hi Jukka,
> Signed-off-by: Jukka Rissanen <[email protected]>
> ---
> include/net/bluetooth/hci_core.h | 27 +++++++++++++++++++++++++++
> net/bluetooth/hci_core.c | 17 +++++++++++++++++
> 2 files changed, 44 insertions(+)
so patches with a no commit body are not acceptable in the Bluetooth subsystem. You need to explain why you are doing this and what it is for. Think of it a bit as story telling so that I can follow it when reviewing the patches.
Regards
Marcel
Create a CoC dynamically instead of one fixed channel for communication
to peer devices.
Signed-off-by: Jukka Rissanen <[email protected]>
---
include/net/bluetooth/hci.h | 1 -
include/net/bluetooth/l2cap.h | 1 -
net/bluetooth/6lowpan.c | 757 +++++++++++++++++++++++++++++++-----------
net/bluetooth/6lowpan.h | 47 ---
net/bluetooth/hci_core.c | 46 +--
net/bluetooth/hci_event.c | 3 -
net/bluetooth/l2cap_core.c | 19 +-
7 files changed, 575 insertions(+), 299 deletions(-)
delete mode 100644 net/bluetooth/6lowpan.h
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 16587dc..3f95aba 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -139,7 +139,6 @@ enum {
HCI_PERIODIC_INQ,
HCI_FAST_CONNECTABLE,
HCI_BREDR_ENABLED,
- HCI_6LOWPAN_ENABLED,
HCI_LE_SCAN_INTERRUPTED,
};
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index 3980b81..510c6ab 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -137,7 +137,6 @@ struct l2cap_conninfo {
#define L2CAP_FC_L2CAP 0x02
#define L2CAP_FC_CONNLESS 0x04
#define L2CAP_FC_A2MP 0x08
-#define L2CAP_FC_6LOWPAN 0x3e /* reserved and temporary value */
/* L2CAP Control Field bit masks */
#define L2CAP_CTRL_SAR 0xC000
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
index adb3ea0..3390b7b 100644
--- a/net/bluetooth/6lowpan.c
+++ b/net/bluetooth/6lowpan.c
@@ -1,5 +1,5 @@
/*
- Copyright (c) 2013 Intel Corp.
+ Copyright (c) 2013-2014 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
@@ -14,6 +14,7 @@
#include <linux/if_arp.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
+#include <linux/debugfs.h>
#include <net/ipv6.h>
#include <net/ip6_route.h>
@@ -25,16 +26,20 @@
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/l2cap.h>
-#include "6lowpan.h"
-
#include "../ieee802154/6lowpan.h" /* for the compression support */
+#define VERSION "1.0"
+
+static struct dentry *lowpan_debugfs;
+static struct l2cap_chan *chan_create(struct l2cap_conn *conn);
+
#define IFACE_NAME_TEMPLATE "bt%d"
#define EUI64_ADDR_LEN 8
struct skb_cb {
struct in6_addr addr;
- struct l2cap_conn *conn;
+ struct l2cap_chan *chan;
+ int status;
};
#define lowpan_cb(skb) ((struct skb_cb *)((skb)->cb))
@@ -48,9 +53,15 @@ struct skb_cb {
static LIST_HEAD(bt_6lowpan_devices);
static DEFINE_RWLOCK(devices_lock);
+/* If psm is set to 0 (default value), then 6lowpan is disabled.
+ * Other values are used to indicate a Protocol Service Multiplexer
+ * value for 6lowpan.
+ */
+static u16 psm_6lowpan;
+
struct lowpan_peer {
struct list_head list;
- struct l2cap_conn *conn;
+ struct l2cap_chan *chan;
/* peer addresses in various formats */
unsigned char eui64_addr[EUI64_ADDR_LEN];
@@ -101,13 +112,26 @@ static inline struct lowpan_peer *peer_lookup_ba(struct lowpan_dev *dev,
ba, type);
list_for_each_entry_safe(peer, tmp, &dev->peers, list) {
- BT_DBG("addr %pMR type %d",
- &peer->conn->hcon->dst, peer->conn->hcon->dst_type);
+ BT_DBG("dst addr %pMR dst type %d",
+ &peer->chan->dst, peer->chan->dst_type);
- if (bacmp(&peer->conn->hcon->dst, ba))
+ if (bacmp(&peer->chan->dst, ba))
continue;
- if (type == peer->conn->hcon->dst_type)
+ if (type == peer->chan->dst_type)
+ return peer;
+ }
+
+ return NULL;
+}
+
+static inline struct lowpan_peer *peer_lookup_chan(struct lowpan_dev *dev,
+ struct l2cap_chan *chan)
+{
+ struct lowpan_peer *peer, *tmp;
+
+ list_for_each_entry_safe(peer, tmp, &dev->peers, list) {
+ if (peer->chan == chan)
return peer;
}
@@ -120,7 +144,7 @@ static inline struct lowpan_peer *peer_lookup_conn(struct lowpan_dev *dev,
struct lowpan_peer *peer, *tmp;
list_for_each_entry_safe(peer, tmp, &dev->peers, list) {
- if (peer->conn == conn)
+ if (peer->chan->conn == conn)
return peer;
}
@@ -185,7 +209,7 @@ static int give_skb_to_upper(struct sk_buff *skb, struct net_device *dev)
}
static int process_data(struct sk_buff *skb, struct net_device *netdev,
- struct l2cap_conn *conn)
+ struct l2cap_chan *chan)
{
const u8 *saddr, *daddr;
u8 iphc0, iphc1;
@@ -196,7 +220,7 @@ static int process_data(struct sk_buff *skb, struct net_device *netdev,
dev = lowpan_dev(netdev);
read_lock_irqsave(&devices_lock, flags);
- peer = peer_lookup_conn(dev, conn);
+ peer = peer_lookup_chan(dev, chan);
read_unlock_irqrestore(&devices_lock, flags);
if (!peer)
goto drop;
@@ -225,7 +249,7 @@ drop:
}
static int recv_pkt(struct sk_buff *skb, struct net_device *dev,
- struct l2cap_conn *conn)
+ struct l2cap_chan *chan)
{
struct sk_buff *local_skb;
int ret;
@@ -269,7 +293,7 @@ static int recv_pkt(struct sk_buff *skb, struct net_device *dev,
if (!local_skb)
goto drop;
- ret = process_data(local_skb, dev, conn);
+ ret = process_data(local_skb, dev, chan);
if (ret != NET_RX_SUCCESS)
goto drop;
@@ -286,140 +310,33 @@ static int recv_pkt(struct sk_buff *skb, struct net_device *dev,
return NET_RX_SUCCESS;
drop:
+ dev->stats.rx_dropped++;
kfree_skb(skb);
return NET_RX_DROP;
}
/* Packet from BT LE device */
-int bt_6lowpan_recv(struct l2cap_conn *conn, struct sk_buff *skb)
+static int bt_6lowpan_chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
{
struct lowpan_dev *dev;
struct lowpan_peer *peer;
int err;
- peer = lookup_peer(conn);
+ peer = lookup_peer(chan->conn);
if (!peer)
return -ENOENT;
- dev = lookup_dev(conn);
+ dev = lookup_dev(chan->conn);
if (!dev || !dev->netdev)
return -ENOENT;
- err = recv_pkt(skb, dev->netdev, conn);
+ err = recv_pkt(skb, dev->netdev, chan);
+
BT_DBG("recv pkt %d", err);
return err;
}
-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_ATOMIC);
- if (!tmp)
- return -ENOMEM;
-
- *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;
-
- /* FIXME: This mtu check should be not needed and atm is only used for
- * testing purposes
- */
- if (conn->mtu > (L2CAP_LE_MIN_MTU + L2CAP_HDR_SIZE))
- 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_ATOMIC);
- if (!skb)
- return ERR_PTR(-ENOMEM);
-
- 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;
-
- skb = create_pdu(conn, msg, len, priority, dev);
- if (IS_ERR(skb))
- return -EINVAL;
-
- 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 0;
-}
-
static void get_dest_bdaddr(struct in6_addr *ip6_daddr,
bdaddr_t *addr, u8 *addr_type)
{
@@ -466,7 +383,7 @@ static int header_create(struct sk_buff *skb, struct net_device *netdev,
if (ipv6_addr_is_multicast(&hdr->daddr)) {
memcpy(&lowpan_cb(skb)->addr, &hdr->daddr,
sizeof(struct in6_addr));
- lowpan_cb(skb)->conn = NULL;
+ lowpan_cb(skb)->chan = NULL;
} else {
unsigned long flags;
@@ -490,7 +407,7 @@ static int header_create(struct sk_buff *skb, struct net_device *netdev,
memcpy(&lowpan_cb(skb)->addr, &hdr->daddr,
sizeof(struct in6_addr));
- lowpan_cb(skb)->conn = peer->conn;
+ lowpan_cb(skb)->chan = peer->chan;
}
saddr = dev->netdev->dev_addr;
@@ -499,14 +416,32 @@ static int header_create(struct sk_buff *skb, struct net_device *netdev,
}
/* Packet to BT LE device */
-static int send_pkt(struct l2cap_conn *conn, const void *saddr,
- const void *daddr, struct sk_buff *skb,
+static int send_pkt(struct l2cap_chan *chan, struct sk_buff *skb,
struct net_device *netdev)
{
- raw_dump_table(__func__, "raw skb data dump before fragmentation",
- skb->data, skb->len);
+ int err;
+
+ /* Remember the skb so that we can send EAGAIN to the caller if
+ * we run out of credits.
+ */
+ chan->data = skb;
+
+ err = l2cap_chan_send(chan, skb->data, skb->len, 0, 0);
+ if (err > 0) {
+ netdev->stats.tx_bytes += err;
+ netdev->stats.tx_packets++;
+ err = 0;
+ } else if (err <= 0) {
+ if (err == 0)
+ err = lowpan_cb(skb)->status;
+
+ if (err == -EAGAIN)
+ netdev->stats.tx_dropped++;
+ else if (err < 0)
+ netdev->stats.tx_errors++;
+ }
- return conn_send(conn, skb->data, skb->len, 0, netdev);
+ return err;
}
static void send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev)
@@ -529,8 +464,7 @@ static void send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev)
list_for_each_entry_safe(pentry, ptmp, &dev->peers, list) {
local_skb = skb_clone(skb, GFP_ATOMIC);
- send_pkt(pentry->conn, netdev->dev_addr,
- pentry->eui64_addr, local_skb, netdev);
+ send_pkt(pentry->chan, local_skb, netdev);
kfree_skb(local_skb);
}
@@ -542,7 +476,6 @@ static void send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev)
static netdev_tx_t bt_xmit(struct sk_buff *skb, struct net_device *netdev)
{
int err = 0;
- unsigned char *eui64_addr;
struct lowpan_dev *dev;
struct lowpan_peer *peer;
bdaddr_t addr;
@@ -557,7 +490,6 @@ static netdev_tx_t bt_xmit(struct sk_buff *skb, struct net_device *netdev)
unsigned long flags;
get_dest_bdaddr(&lowpan_cb(skb)->addr, &addr, &addr_type);
- eui64_addr = lowpan_cb(skb)->addr.s6_addr + 8;
dev = lowpan_dev(netdev);
read_lock_irqsave(&devices_lock, flags);
@@ -567,9 +499,10 @@ static netdev_tx_t bt_xmit(struct sk_buff *skb, struct net_device *netdev)
BT_DBG("xmit from %s to %pMR (%pI6c) peer %p", netdev->name,
&addr, &lowpan_cb(skb)->addr, peer);
- if (peer && peer->conn)
- err = send_pkt(peer->conn, netdev->dev_addr,
- eui64_addr, skb, netdev);
+ if (peer && peer->chan)
+ err = send_pkt(peer->chan, skb, netdev);
+ else
+ err = -ENOENT;
}
dev_kfree_skb(skb);
@@ -661,26 +594,46 @@ static bool is_bt_6lowpan(struct hci_conn *hcon)
if (hcon->type != LE_LINK)
return false;
- return test_bit(HCI_CONN_6LOWPAN, &hcon->flags);
+ if (!psm_6lowpan)
+ return false;
+
+ return true;
}
-static int add_peer_conn(struct l2cap_conn *conn, struct lowpan_dev *dev)
+static struct l2cap_chan *chan_open(struct l2cap_chan *pchan)
+{
+ struct l2cap_chan *chan;
+
+ chan = chan_create(pchan->conn);
+ if (!chan)
+ return NULL;
+
+ chan->remote_mps = chan->omtu;
+ chan->mps = chan->omtu;
+
+ chan->state = BT_CONNECTED;
+
+ return chan;
+}
+
+static struct l2cap_chan *add_peer_chan(struct l2cap_chan *chan,
+ struct lowpan_dev *dev)
{
struct lowpan_peer *peer;
unsigned long flags;
peer = kzalloc(sizeof(*peer), GFP_ATOMIC);
if (!peer)
- return -ENOMEM;
+ return NULL;
- peer->conn = conn;
+ peer->chan = chan;
memset(&peer->peer_addr, 0, sizeof(struct in6_addr));
/* RFC 2464 ch. 5 */
peer->peer_addr.s6_addr[0] = 0xFE;
peer->peer_addr.s6_addr[1] = 0x80;
- set_addr((u8 *)&peer->peer_addr.s6_addr + 8, conn->hcon->dst.b,
- conn->hcon->dst_type);
+ set_addr((u8 *)&peer->peer_addr.s6_addr + 8, chan->dst.b,
+ chan->dst_type);
memcpy(&peer->eui64_addr, (u8 *)&peer->peer_addr.s6_addr + 8,
EUI64_ADDR_LEN);
@@ -701,40 +654,24 @@ static int add_peer_conn(struct l2cap_conn *conn, struct lowpan_dev *dev)
INIT_DELAYED_WORK(&dev->notify_peers, do_notify_peers);
schedule_delayed_work(&dev->notify_peers, msecs_to_jiffies(100));
- return 0;
+ return peer->chan;
}
-/* 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)
+static int setup_netdev(struct l2cap_chan *chan, struct lowpan_dev **dev)
{
- struct lowpan_peer *peer = NULL;
- struct lowpan_dev *dev;
struct net_device *netdev;
int err = 0;
unsigned long flags;
- if (!is_bt_6lowpan(conn->hcon))
- return 0;
-
- peer = lookup_peer(conn);
- if (peer)
- return -EEXIST;
-
- dev = lookup_dev(conn);
- if (dev)
- return add_peer_conn(conn, dev);
-
- netdev = alloc_netdev(sizeof(*dev), IFACE_NAME_TEMPLATE, netdev_setup);
+ netdev = alloc_netdev(sizeof(struct lowpan_dev), IFACE_NAME_TEMPLATE,
+ netdev_setup);
if (!netdev)
return -ENOMEM;
- set_dev_addr(netdev, &conn->hcon->src, conn->hcon->src_type);
+ set_dev_addr(netdev, &chan->src, chan->src_type);
netdev->netdev_ops = &netdev_ops;
- SET_NETDEV_DEV(netdev, &conn->hcon->dev);
+ SET_NETDEV_DEV(netdev, &chan->conn->hcon->dev);
SET_NETDEV_DEVTYPE(netdev, &bt_type);
err = register_netdev(netdev);
@@ -744,28 +681,54 @@ int bt_6lowpan_add_conn(struct l2cap_conn *conn)
goto out;
}
- BT_DBG("ifindex %d peer bdaddr %pMR my addr %pMR",
- netdev->ifindex, &conn->hcon->dst, &conn->hcon->src);
+ BT_DBG("ifindex %d peer bdaddr %pMR type %d my addr %pMR type %d",
+ netdev->ifindex, &chan->dst, chan->dst_type,
+ &chan->src, chan->src_type);
set_bit(__LINK_STATE_PRESENT, &netdev->state);
- dev = netdev_priv(netdev);
- dev->netdev = netdev;
- dev->hdev = conn->hcon->hdev;
- INIT_LIST_HEAD(&dev->peers);
+ *dev = netdev_priv(netdev);
+ (*dev)->netdev = netdev;
+ (*dev)->hdev = chan->conn->hcon->hdev;
+ INIT_LIST_HEAD(&(*dev)->peers);
write_lock_irqsave(&devices_lock, flags);
- INIT_LIST_HEAD(&dev->list);
- list_add(&dev->list, &bt_6lowpan_devices);
+ INIT_LIST_HEAD(&(*dev)->list);
+ list_add(&(*dev)->list, &bt_6lowpan_devices);
write_unlock_irqrestore(&devices_lock, flags);
- ifup(netdev);
-
- return add_peer_conn(conn, dev);
+ return 0;
out:
return err;
}
+static inline void bt_6lowpan_chan_ready_cb(struct l2cap_chan *chan)
+{
+ struct lowpan_dev *dev;
+
+ dev = lookup_dev(chan->conn);
+
+ BT_DBG("chan %p conn %p dev %p", chan, chan->conn, dev);
+
+ if (!dev) {
+ if (setup_netdev(chan, &dev) < 0) {
+ l2cap_chan_del(chan, -ENOENT);
+ return;
+ }
+ }
+
+ add_peer_chan(chan, dev);
+ ifup(dev->netdev);
+}
+
+static inline
+struct l2cap_chan *bt_6lowpan_chan_new_connection_cb(struct l2cap_chan *chan)
+{
+ BT_DBG("chan %p", chan);
+
+ return chan_open(chan);
+}
+
static void delete_netdev(struct work_struct *work)
{
struct lowpan_dev *entry = container_of(work, struct lowpan_dev,
@@ -776,26 +739,39 @@ static void delete_netdev(struct work_struct *work)
/* The entry pointer is deleted in device_event() */
}
-int bt_6lowpan_del_conn(struct l2cap_conn *conn)
+static void bt_6lowpan_chan_close_cb(struct l2cap_chan *chan)
{
struct lowpan_dev *entry, *tmp;
struct lowpan_dev *dev = NULL;
struct lowpan_peer *peer;
int err = -ENOENT;
unsigned long flags;
- bool last = false;
+ bool last = false, removed = true;
+
+ BT_DBG("chan %p conn %p", chan, chan->conn);
- if (!conn || !is_bt_6lowpan(conn->hcon))
- return 0;
+ if (chan->conn && chan->conn->hcon) {
+ if (!is_bt_6lowpan(chan->conn->hcon))
+ return;
+
+ /*
+ * If conn is set, then the netdev is also there and we should
+ * not remove it.
+ */
+ removed = false;
+ }
write_lock_irqsave(&devices_lock, flags);
list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) {
dev = lowpan_dev(entry->netdev);
- peer = peer_lookup_conn(dev, conn);
+ peer = peer_lookup_chan(dev, chan);
if (peer) {
last = peer_del(dev, peer);
err = 0;
+ BT_DBG("dev %p removing %speer %p", dev,
+ last ? "last " : "1 ", peer);
+ kfree(peer);
break;
}
}
@@ -805,18 +781,407 @@ int bt_6lowpan_del_conn(struct l2cap_conn *conn)
cancel_delayed_work_sync(&dev->notify_peers);
- /* bt_6lowpan_del_conn() 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);
+ if (!removed) {
+ INIT_WORK(&entry->delete_netdev, delete_netdev);
+ schedule_work(&entry->delete_netdev);
+ }
} else {
write_unlock_irqrestore(&devices_lock, flags);
}
+ return;
+}
+
+static void bt_6lowpan_chan_state_change_cb(struct l2cap_chan *chan, int state,
+ int err)
+{
+ BT_DBG("chan %p conn %p", chan, chan->conn);
+}
+
+static struct sk_buff *bt_6lowpan_chan_alloc_skb_cb(struct l2cap_chan *chan,
+ unsigned long len, int nb)
+{
+ return bt_skb_alloc(len, GFP_ATOMIC);
+}
+
+static void bt_6lowpan_chan_suspend_cb(struct l2cap_chan *chan)
+{
+ struct sk_buff *skb = chan->data;
+
+ BT_DBG("chan %p conn %p skb %p", chan, chan->conn, skb);
+
+ lowpan_cb(skb)->status = -EAGAIN;
+}
+
+static void bt_6lowpan_chan_resume_cb(struct l2cap_chan *chan)
+{
+ struct sk_buff *skb = chan->data;
+
+ BT_DBG("chan %p conn %p skb %p", chan, chan->conn, skb);
+
+ lowpan_cb(skb)->status = 0;
+}
+
+static long bt_l2cap_chan_get_sndtimeo(struct l2cap_chan *chan)
+{
+ return msecs_to_jiffies(1000);
+}
+
+static void bt_6lowpan_chan_teardown_cb(struct l2cap_chan *chan, int err)
+{
+ BT_DBG("chan %p conn %p err %d", chan, chan->conn, err);
+}
+
+static struct l2cap_ops bt_6lowpan_chan_ops = {
+ .name = "L2CAP 6LoWPAN channel",
+ .new_connection = bt_6lowpan_chan_new_connection_cb,
+ .recv = bt_6lowpan_chan_recv_cb,
+ .teardown = bt_6lowpan_chan_teardown_cb,
+ .close = bt_6lowpan_chan_close_cb,
+ .state_change = bt_6lowpan_chan_state_change_cb,
+ .ready = bt_6lowpan_chan_ready_cb,
+ .resume = bt_6lowpan_chan_resume_cb,
+ .suspend = bt_6lowpan_chan_suspend_cb,
+ .get_sndtimeo = bt_l2cap_chan_get_sndtimeo,
+ .alloc_skb = bt_6lowpan_chan_alloc_skb_cb,
+
+ .defer = l2cap_chan_no_defer,
+ .set_shutdown = l2cap_chan_no_set_shutdown,
+};
+
+static struct l2cap_chan *chan_create(struct l2cap_conn *conn)
+{
+ struct l2cap_chan *chan;
+
+ chan = l2cap_chan_create();
+ if (!chan)
+ return NULL;
+
+ l2cap_chan_set_defaults(chan);
+
+ chan->chan_type = L2CAP_CHAN_CONN_ORIENTED;
+ chan->mode = L2CAP_MODE_LE_FLOWCTL;
+ chan->omtu = 65535;
+ chan->imtu = chan->omtu;
+ chan->ops = &bt_6lowpan_chan_ops;
+
+ return chan;
+}
+
+static struct l2cap_chan *bt_6lowpan_channel_create(struct l2cap_conn *conn)
+{
+ struct lowpan_peer *peer;
+
+ if (!is_bt_6lowpan(conn->hcon))
+ return NULL;
+
+ BT_DBG("conn %p", conn);
+
+ peer = lookup_peer(conn);
+ if (peer) {
+ BT_DBG("6LoWPAN connection and channel already exists");
+ return NULL;
+ }
+
+ return chan_create(conn);
+}
+
+static inline __u8 bdaddr_type(__u8 link_type, __u8 type)
+{
+ if (link_type == LE_LINK) {
+ if (type == ADDR_LE_DEV_PUBLIC)
+ return BDADDR_LE_PUBLIC;
+ else
+ return BDADDR_LE_RANDOM;
+ }
+
+ return BDADDR_BREDR;
+}
+
+static int bt_6lowpan_connect(struct l2cap_conn *conn, u8 dst_type)
+{
+ struct hci_conn *hcon = conn->hcon;
+ struct l2cap_chan *pchan;
+ int err;
+
+ BT_DBG("conn %p dst %pMR type %d user %d", conn, &hcon->dst,
+ hcon->dst_type, dst_type);
+
+ if (hci_blacklist_lookup(hcon->hdev, &hcon->dst, hcon->dst_type))
+ return -EACCES;
+
+ pchan = bt_6lowpan_channel_create(conn);
+ if (!pchan)
+ return -EINVAL;
+
+ err = l2cap_chan_connect(pchan, cpu_to_le16(psm_6lowpan), 0,
+ &hcon->dst, dst_type);
+
+ BT_DBG("chan %p err %d", pchan, err);
+
return err;
}
+static int bt_6lowpan_disconnect(struct l2cap_conn *conn, u8 dst_type)
+{
+ struct lowpan_peer *peer;
+
+ BT_DBG("conn %p dst type %d", conn, dst_type);
+
+ peer = lookup_peer(conn);
+ if (!peer)
+ return -ENOENT;
+
+ l2cap_chan_close(peer->chan, ENOENT);
+
+ return 0;
+}
+
+static int bt_6lowpan_listen(struct l2cap_conn *conn)
+{
+ struct hci_conn *hcon = conn->hcon;
+ struct l2cap_chan *pchan;
+ int err;
+
+ if (!conn)
+ return -ENOENT;
+
+ pchan = bt_6lowpan_channel_create(conn);
+ if (!pchan)
+ return -ENOENT;
+
+ pchan->state = BT_LISTEN;
+ pchan->src_type = bdaddr_type(hcon->type, hcon->src_type);
+
+ BT_DBG("psm 0x%04x chan %p src type %d", psm_6lowpan, pchan,
+ pchan->src_type);
+
+ err = l2cap_add_psm(pchan, &hcon->src,
+ cpu_to_le16(psm_6lowpan));
+ if (err) {
+ BT_ERR("psm cannot be added err %d", err);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int get_l2cap_conn(struct hci_dev *hdev, char *buf,
+ bdaddr_t *addr, u8 *addr_type,
+ struct l2cap_conn **conn)
+{
+ struct hci_conn *hcon;
+ int n;
+
+ n = sscanf(buf, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx %hhu",
+ &addr->b[5], &addr->b[4], &addr->b[3],
+ &addr->b[2], &addr->b[1], &addr->b[0],
+ addr_type);
+
+ if (n < 7)
+ return -EINVAL;
+
+ hci_dev_lock(hdev);
+ hcon = hci_conn_hash_lookup_ba(hdev, LE_LINK, addr);
+ hci_dev_unlock(hdev);
+
+ if (!hcon)
+ return -ENOENT;
+
+ *conn = (struct l2cap_conn *)hcon->l2cap_data;
+
+ BT_DBG("conn %p dst %pMR type %d", *conn, &hcon->dst, hcon->dst_type);
+
+ return 0;
+}
+
+static void disconnect_all_peers(struct hci_dev *hdev)
+{
+ struct lowpan_dev *entry, *tmp_dev;
+ struct lowpan_peer *peer, *tmp_peer, *new_peer;
+ struct list_head peers;
+ unsigned long flags;
+
+ INIT_LIST_HEAD(&peers);
+
+ /* We make a separate list of peers as the close_cb() will
+ * modify the device peers list so it is better not to mess
+ * with the same list at the same time.
+ */
+
+ hci_dev_lock(hdev);
+
+ read_lock_irqsave(&devices_lock, flags);
+
+ list_for_each_entry_safe(entry, tmp_dev, &bt_6lowpan_devices, list) {
+ list_for_each_entry_safe(peer, tmp_peer, &entry->peers, list) {
+ new_peer = kmalloc(sizeof(*new_peer), GFP_ATOMIC);
+ if (!new_peer)
+ break;
+
+ new_peer->chan = peer->chan;
+ INIT_LIST_HEAD(&new_peer->list);
+
+ list_add(&new_peer->list, &peers);
+ }
+ }
+
+ read_unlock_irqrestore(&devices_lock, flags);
+
+ hci_dev_unlock(hdev);
+
+ list_for_each_entry_safe(peer, tmp_peer, &peers, list) {
+ l2cap_chan_close(peer->chan, ENOENT);
+ kfree(peer);
+ }
+}
+
+static ssize_t lowpan_write(struct file *fp, const char __user *user_buffer,
+ size_t count, loff_t *position)
+{
+ struct seq_file *f = fp->private_data;
+ struct hci_dev *hdev = f->private;
+ char buf[32];
+ size_t buf_size = min(count, sizeof(buf) - 1);
+ int ret;
+ bdaddr_t addr;
+ u8 addr_type;
+ struct l2cap_conn *conn;
+
+ if (copy_from_user(buf, user_buffer, buf_size))
+ return -EFAULT;
+
+ buf[buf_size] = '\0';
+
+ if (memcmp(buf, "psm ", 4) == 0) {
+ unsigned long value;
+ u16 psm;
+
+ ret = kstrtoul(&buf[4], 0, &value);
+ if (ret < 0)
+ return ret;
+
+ psm = value;
+ if (psm == 0 || psm_6lowpan != psm)
+ /* Disconnect existing connections if 6lowpan is
+ * disabled (psm = 0), or if psm changes.
+ */
+ disconnect_all_peers(hdev);
+
+ psm_6lowpan = psm;
+
+ return count;
+ }
+
+ if (memcmp(buf, "connect ", 8) == 0) {
+
+ ret = get_l2cap_conn(hdev, &buf[8], &addr, &addr_type, &conn);
+ if (ret < 0)
+ return ret;
+
+ ret = bt_6lowpan_connect(conn, addr_type);
+ if (ret < 0)
+ return ret;
+
+ return count;
+ }
+
+ if (memcmp(buf, "disconnect ", 11) == 0) {
+
+ ret = get_l2cap_conn(hdev, &buf[11], &addr, &addr_type, &conn);
+ if (ret < 0)
+ return ret;
+
+ ret = bt_6lowpan_disconnect(conn, addr_type);
+ if (ret < 0)
+ return ret;
+
+ return count;
+ }
+
+ return count;
+}
+
+static int lowpan_show(struct seq_file *f, void *ptr)
+{
+ struct hci_dev *hdev = f->private;
+ struct lowpan_dev *entry, *tmp_dev;
+ struct lowpan_peer *peer, *tmp_peer;
+ unsigned long flags;
+
+ seq_printf(f, "psm %u\n", psm_6lowpan);
+
+ hci_dev_lock(hdev);
+ read_lock_irqsave(&devices_lock, flags);
+
+ list_for_each_entry_safe(entry, tmp_dev, &bt_6lowpan_devices, list) {
+ list_for_each_entry_safe(peer, tmp_peer, &entry->peers, list)
+ seq_printf(f, "%pMR (type %u)\n",
+ &peer->chan->dst, peer->chan->dst_type);
+ }
+
+ read_unlock_irqrestore(&devices_lock, flags);
+ hci_dev_unlock(hdev);
+
+ return 0;
+}
+
+static int lowpan_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, lowpan_show, inode->i_private);
+}
+
+static const struct file_operations lowpan_debugfs_fops = {
+ .open = lowpan_open,
+ .read = seq_read,
+ .write = lowpan_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+struct lowpan_check {
+ struct hci_dev *hdev;
+ struct work_struct setup_6lowpan;
+};
+
+static void setup_6lowpan(struct work_struct *work)
+{
+ struct lowpan_check *check = container_of(work, struct lowpan_check,
+ setup_6lowpan);
+ struct hci_dev *hdev = check->hdev;
+
+ kfree(check);
+
+ lowpan_debugfs = debugfs_create_file("6lowpan", 0644, hdev->debugfs,
+ hdev, &lowpan_debugfs_fops);
+}
+
+static void lowpan_connect_hcon(struct hci_conn *hcon)
+{
+ struct l2cap_conn *conn = hcon->l2cap_data;
+
+ if (!conn || !psm_6lowpan)
+ return;
+
+ bt_6lowpan_listen(conn);
+}
+
+static void lowpan_create_hci(struct hci_dev *hdev)
+{
+ struct lowpan_check *check;
+
+ check = kmalloc(sizeof(struct lowpan_check), GFP_ATOMIC);
+ if (!check)
+ return;
+
+ BT_DBG("hdev %p", hdev);
+
+ check->hdev = hdev;
+
+ INIT_WORK(&check->setup_6lowpan, setup_6lowpan);
+ schedule_work(&check->setup_6lowpan);
+}
+
static int device_event(struct notifier_block *unused,
unsigned long event, void *ptr)
{
@@ -849,12 +1214,24 @@ static struct notifier_block bt_6lowpan_dev_notifier = {
.notifier_call = device_event,
};
+static struct hci_cb lowpan_cb = {
+ .name = "6LoWPAN",
+ .create_cfm = lowpan_create_hci,
+ .connect_cfm = lowpan_connect_hcon,
+};
+
int bt_6lowpan_init(void)
{
+ hci_register_cb(&lowpan_cb);
+
return register_netdevice_notifier(&bt_6lowpan_dev_notifier);
}
void bt_6lowpan_cleanup(void)
{
+ debugfs_remove(lowpan_debugfs);
+
+ hci_unregister_cb(&lowpan_cb);
+
unregister_netdevice_notifier(&bt_6lowpan_dev_notifier);
}
diff --git a/net/bluetooth/6lowpan.h b/net/bluetooth/6lowpan.h
deleted file mode 100644
index 5d281f1..0000000
--- a/net/bluetooth/6lowpan.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- 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/errno.h>
-#include <linux/skbuff.h>
-#include <net/bluetooth/l2cap.h>
-
-#if IS_ENABLED(CONFIG_BT_6LOWPAN)
-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);
-#else
-static int bt_6lowpan_recv(struct l2cap_conn *conn, struct sk_buff *skb)
-{
- return -EOPNOTSUPP;
-}
-static int bt_6lowpan_add_conn(struct l2cap_conn *conn)
-{
- return -EOPNOTSUPP;
-}
-int bt_6lowpan_del_conn(struct l2cap_conn *conn)
-{
- return -EOPNOTSUPP;
-}
-static int bt_6lowpan_init(void)
-{
- return -EOPNOTSUPP;
-}
-static void bt_6lowpan_cleanup(void) { }
-#endif
-
-#endif /* __6LOWPAN_H */
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index a5d6698..0961e20 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -928,49 +928,6 @@ static int adv_channel_map_get(void *data, u64 *val)
DEFINE_SIMPLE_ATTRIBUTE(adv_channel_map_fops, adv_channel_map_get,
adv_channel_map_set, "%llu\n");
-static ssize_t lowpan_read(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct hci_dev *hdev = file->private_data;
- char buf[3];
-
- buf[0] = test_bit(HCI_6LOWPAN_ENABLED, &hdev->dev_flags) ? 'Y' : 'N';
- buf[1] = '\n';
- buf[2] = '\0';
- return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
-}
-
-static ssize_t lowpan_write(struct file *fp, const char __user *user_buffer,
- size_t count, loff_t *position)
-{
- struct hci_dev *hdev = fp->private_data;
- bool enable;
- char buf[32];
- size_t buf_size = min(count, (sizeof(buf)-1));
-
- if (copy_from_user(buf, user_buffer, buf_size))
- return -EFAULT;
-
- buf[buf_size] = '\0';
-
- if (strtobool(buf, &enable) < 0)
- return -EINVAL;
-
- if (enable == test_bit(HCI_6LOWPAN_ENABLED, &hdev->dev_flags))
- return -EALREADY;
-
- change_bit(HCI_6LOWPAN_ENABLED, &hdev->dev_flags);
-
- return count;
-}
-
-static const struct file_operations lowpan_debugfs_fops = {
- .open = simple_open,
- .read = lowpan_read,
- .write = lowpan_write,
- .llseek = default_llseek,
-};
-
static int le_auto_conn_show(struct seq_file *sf, void *ptr)
{
struct hci_dev *hdev = sf->private;
@@ -1881,8 +1838,6 @@ static int __hci_init(struct hci_dev *hdev)
hdev, &conn_max_interval_fops);
debugfs_create_file("adv_channel_map", 0644, hdev->debugfs,
hdev, &adv_channel_map_fops);
- debugfs_create_file("6lowpan", 0644, hdev->debugfs, hdev,
- &lowpan_debugfs_fops);
debugfs_create_file("le_auto_conn", 0644, hdev->debugfs, hdev,
&le_auto_conn_fops);
debugfs_create_u16("discov_interleaved_timeout", 0644,
@@ -3325,6 +3280,7 @@ struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev,
return NULL;
}
+EXPORT_SYMBOL_GPL(hci_blacklist_lookup);
static void hci_blacklist_clear(struct hci_dev *hdev)
{
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index b1bfa3f..7ae192a 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -4043,9 +4043,6 @@ 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 3a7f560..6774e40 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -40,7 +40,6 @@
#include "smp.h"
#include "a2mp.h"
#include "amp.h"
-#include "6lowpan.h"
#define LE_FLOWCTL_MAX_CREDITS 65535
@@ -205,6 +204,7 @@ done:
write_unlock(&chan_list_lock);
return err;
}
+EXPORT_SYMBOL_GPL(l2cap_add_psm);
int l2cap_add_scid(struct l2cap_chan *chan, __u16 scid)
{
@@ -437,6 +437,7 @@ struct l2cap_chan *l2cap_chan_create(void)
return chan;
}
+EXPORT_SYMBOL_GPL(l2cap_chan_create);
static void l2cap_chan_destroy(struct kref *kref)
{
@@ -482,6 +483,7 @@ void l2cap_chan_set_defaults(struct l2cap_chan *chan)
set_bit(FLAG_FORCE_ACTIVE, &chan->flags);
}
+EXPORT_SYMBOL_GPL(l2cap_chan_set_defaults);
static void l2cap_le_flowctl_init(struct l2cap_chan *chan)
{
@@ -614,6 +616,7 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err)
return;
}
+EXPORT_SYMBOL_GPL(l2cap_chan_del);
void l2cap_conn_update_id_addr(struct hci_conn *hcon)
{
@@ -717,6 +720,7 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason)
break;
}
}
+EXPORT_SYMBOL(l2cap_chan_close);
static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan)
{
@@ -1460,8 +1464,6 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
BT_DBG("");
- bt_6lowpan_add_conn(conn);
-
/* Check if we have socket listening on cid */
pchan = l2cap_global_chan_by_scid(BT_LISTEN, L2CAP_CID_ATT,
&hcon->src, &hcon->dst);
@@ -2564,6 +2566,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, unsigned char *msg, size_t len,
return err;
}
+EXPORT_SYMBOL_GPL(l2cap_chan_send);
static void l2cap_send_srej(struct l2cap_chan *chan, u16 txseq)
{
@@ -6942,10 +6945,6 @@ 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;
@@ -7192,6 +7191,7 @@ done:
hci_dev_put(hdev);
return err;
}
+EXPORT_SYMBOL_GPL(l2cap_chan_connect);
/* ---- L2CAP interface with lower layer (HCI) ---- */
@@ -7254,8 +7254,6 @@ void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason)
{
BT_DBG("hcon %p reason %d", hcon, reason);
- bt_6lowpan_del_conn(hcon->l2cap_data);
-
l2cap_conn_del(hcon, bt_to_errno(reason));
}
@@ -7538,14 +7536,11 @@ int __init l2cap_init(void)
debugfs_create_u16("l2cap_le_default_mps", 0466, bt_debugfs,
&le_default_mps);
- bt_6lowpan_init();
-
return 0;
}
void l2cap_exit(void)
{
- bt_6lowpan_cleanup();
debugfs_remove(l2cap_debugfs);
l2cap_cleanup_sockets();
}
--
1.8.3.1
Signed-off-by: Jukka Rissanen <[email protected]>
---
net/bluetooth/6lowpan.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 54 insertions(+)
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
index 04e0501..4310eb1 100644
--- a/net/bluetooth/6lowpan.c
+++ b/net/bluetooth/6lowpan.c
@@ -592,6 +592,17 @@ static void ifup(struct net_device *netdev)
rtnl_unlock();
}
+static void ifdown(struct net_device *netdev)
+{
+ int err;
+
+ rtnl_lock();
+ err = dev_close(netdev);
+ if (err < 0)
+ BT_INFO("iface %s cannot be closed (%d)", netdev->name, err);
+ rtnl_unlock();
+}
+
static void do_notify_peers(struct work_struct *work)
{
struct lowpan_dev *dev = container_of(work, struct lowpan_dev,
@@ -721,6 +732,9 @@ static inline void bt_6lowpan_chan_ready_cb(struct l2cap_chan *chan)
}
}
+ if (!try_module_get(THIS_MODULE))
+ return;
+
add_peer_chan(chan, dev);
ifup(dev->netdev);
}
@@ -780,11 +794,15 @@ static void bt_6lowpan_chan_close_cb(struct l2cap_chan *chan)
}
}
+ module_put(THIS_MODULE);
+
if (!err && last && dev && !atomic_read(&dev->peer_count)) {
write_unlock_irqrestore(&devices_lock, flags);
cancel_delayed_work_sync(&dev->notify_peers);
+ ifdown(dev->netdev);
+
if (!removed) {
INIT_WORK(&entry->delete_netdev, delete_netdev);
schedule_work(&entry->delete_netdev);
@@ -1186,6 +1204,38 @@ static void lowpan_create_hci(struct hci_dev *hdev)
schedule_work(&check->setup_6lowpan);
}
+static void disconnect_devices(void)
+{
+ struct lowpan_dev *entry, *tmp, *new_dev;
+ struct list_head devices;
+ unsigned long flags;
+
+ INIT_LIST_HEAD(&devices);
+
+ read_lock_irqsave(&devices_lock, flags);
+
+ list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) {
+ new_dev = kmalloc(sizeof(*new_dev), GFP_ATOMIC);
+ if (!new_dev)
+ break;
+
+ new_dev->netdev = entry->netdev;
+ INIT_LIST_HEAD(&new_dev->list);
+
+ list_add(&new_dev->list, &devices);
+ }
+
+ read_unlock_irqrestore(&devices_lock, flags);
+
+ list_for_each_entry_safe(entry, tmp, &devices, list) {
+ ifdown(entry->netdev);
+ BT_DBG("Unregistering netdev %s %p",
+ entry->netdev->name, entry->netdev);
+ unregister_netdev(entry->netdev);
+ kfree(entry);
+ }
+}
+
static int device_event(struct notifier_block *unused,
unsigned long event, void *ptr)
{
@@ -1202,6 +1252,8 @@ static int device_event(struct notifier_block *unused,
list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices,
list) {
if (entry->netdev == netdev) {
+ BT_DBG("Unregistered netdev %s %p",
+ netdev->name, netdev);
list_del(&entry->list);
kfree(entry);
break;
@@ -1237,6 +1289,8 @@ static void __exit bt_6lowpan_cleanup(void)
hci_unregister_cb(&lowpan_cb);
+ disconnect_devices();
+
unregister_netdevice_notifier(&bt_6lowpan_dev_notifier);
}
--
1.8.3.1
The universal/local bit handling was incorrectly done in the code.
So when setting EUI address from BD address we do this:
- If BD address type is PUBLIC, then we clear the universal bit
in EUI address. If the address type is RANDOM, then the universal
bit is set (BT 6lowpan draft chapter 3.2.2)
- After this we invert the universal/local bit according to RFC 2464
When figuring out BD address we do the reverse:
- Take EUI address from stateless IPv6 address, invert the
universal/local bit according to RFC 2464
- If universal bit is 1 in this modified EUI address, then address
type is set to RANDOM, otherwise it is PUBLIC
Note that 6lowpan_iphc.[ch] does the final toggling of U/L bit
before sending or receiving the network packet.
Signed-off-by: Jukka Rissanen <[email protected]>
---
net/bluetooth/6lowpan.c | 65 ++++++++++++++++++++++++++-----------------------
1 file changed, 34 insertions(+), 31 deletions(-)
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
index 3390b7b..46cc298 100644
--- a/net/bluetooth/6lowpan.c
+++ b/net/bluetooth/6lowpan.c
@@ -337,12 +337,18 @@ static int bt_6lowpan_chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
return err;
}
-static void get_dest_bdaddr(struct in6_addr *ip6_daddr,
- bdaddr_t *addr, u8 *addr_type)
+static u8 get_addr_type_from_eui64(u8 byte)
{
- u8 *eui64;
+ /* Is universal(0) or local(1) bit, */
+ if (byte & 0x02)
+ return BDADDR_LE_RANDOM;
- eui64 = ip6_daddr->s6_addr + 8;
+ return BDADDR_LE_PUBLIC;
+}
+
+static void copy_to_bdaddr(struct in6_addr *ip6_daddr, bdaddr_t *addr)
+{
+ u8 *eui64 = ip6_daddr->s6_addr + 8;
addr->b[0] = eui64[7];
addr->b[1] = eui64[6];
@@ -350,16 +356,19 @@ static void get_dest_bdaddr(struct in6_addr *ip6_daddr,
addr->b[3] = eui64[2];
addr->b[4] = eui64[1];
addr->b[5] = eui64[0];
+}
- addr->b[5] ^= 2;
+static void convert_dest_bdaddr(struct in6_addr *ip6_daddr,
+ bdaddr_t *addr, u8 *addr_type)
+{
+ copy_to_bdaddr(ip6_daddr, addr);
- /* Set universal/local bit to 0 */
- if (addr->b[5] & 1) {
- addr->b[5] &= ~1;
- *addr_type = ADDR_LE_DEV_PUBLIC;
- } else {
- *addr_type = ADDR_LE_DEV_RANDOM;
- }
+ /* We need to toggle the U/L bit that we got from IPv6 address
+ * so that we get the proper address and type of the BD address.
+ */
+ addr->b[5] ^= 0x02;
+
+ *addr_type = get_addr_type_from_eui64(addr->b[5]);
}
static int header_create(struct sk_buff *skb, struct net_device *netdev,
@@ -390,9 +399,11 @@ static int header_create(struct sk_buff *skb, struct net_device *netdev,
/* Get destination BT device from skb.
* If there is no such peer then discard the packet.
*/
- get_dest_bdaddr(&hdr->daddr, &addr, &addr_type);
+ convert_dest_bdaddr(&hdr->daddr, &addr, &addr_type);
- BT_DBG("dest addr %pMR type %d", &addr, addr_type);
+ BT_DBG("dest addr %pMR type %s(%d) IP %pI6c", &addr,
+ addr_type == BDADDR_LE_PUBLIC ? "PUBLIC" : "RANDOM",
+ addr_type, &hdr->daddr);
read_lock_irqsave(&devices_lock, flags);
peer = peer_lookup_ba(dev, &addr, addr_type);
@@ -489,15 +500,17 @@ static netdev_tx_t bt_xmit(struct sk_buff *skb, struct net_device *netdev)
} else {
unsigned long flags;
- get_dest_bdaddr(&lowpan_cb(skb)->addr, &addr, &addr_type);
+ convert_dest_bdaddr(&lowpan_cb(skb)->addr, &addr, &addr_type);
dev = lowpan_dev(netdev);
read_lock_irqsave(&devices_lock, flags);
peer = peer_lookup_ba(dev, &addr, addr_type);
read_unlock_irqrestore(&devices_lock, flags);
- BT_DBG("xmit from %s to %pMR (%pI6c) peer %p", netdev->name,
- &addr, &lowpan_cb(skb)->addr, peer);
+ BT_DBG("xmit %s to %pMR type %s(%d) IP %pI6c peer %p",
+ netdev->name, &addr,
+ addr_type == BDADDR_LE_PUBLIC ? "PUBLIC" : "RANDOM",
+ addr_type, &lowpan_cb(skb)->addr, peer);
if (peer && peer->chan)
err = send_pkt(peer->chan, skb, netdev);
@@ -553,13 +566,11 @@ static void set_addr(u8 *eui, u8 *addr, u8 addr_type)
eui[6] = addr[1];
eui[7] = addr[0];
- eui[0] ^= 2;
-
- /* Universal/local bit set, RFC 4291 */
- if (addr_type == ADDR_LE_DEV_PUBLIC)
- eui[0] |= 1;
+ /* Universal/local bit set, BT 6lowpan draft ch. 3.2.1 */
+ if (addr_type == BDADDR_LE_PUBLIC)
+ eui[0] &= ~2;
else
- eui[0] &= ~1;
+ eui[0] |= 2;
}
static void set_dev_addr(struct net_device *netdev, bdaddr_t *addr,
@@ -567,7 +578,6 @@ static void set_dev_addr(struct net_device *netdev, bdaddr_t *addr,
{
netdev->addr_assign_type = NET_ADDR_PERM;
set_addr(netdev->dev_addr, addr->b, addr_type);
- netdev->dev_addr[0] ^= 2;
}
static void ifup(struct net_device *netdev)
@@ -637,13 +647,6 @@ static struct l2cap_chan *add_peer_chan(struct l2cap_chan *chan,
memcpy(&peer->eui64_addr, (u8 *)&peer->peer_addr.s6_addr + 8,
EUI64_ADDR_LEN);
- peer->eui64_addr[0] ^= 2; /* second bit-flip (Universe/Local)
- * is done according RFC2464
- */
-
- raw_dump_inline(__func__, "peer IPv6 address",
- (unsigned char *)&peer->peer_addr, 16);
- raw_dump_inline(__func__, "peer EUI64 address", peer->eui64_addr, 8);
write_lock_irqsave(&devices_lock, flags);
INIT_LIST_HEAD(&peer->list);
--
1.8.3.1
Instead of adding the 6LoWPAN functionality to Bluetooth module,
we create a separate kernel module for it.
Usage:
In the client side do this:
# modprobe bluetooth_6lowpan
# echo 'psm 62' > /sys/kernel/debug/bluetooth/hci0/6lowpan
# hciconfig hci0 leadv
In the server side do this:
# modprobe bluetooth_6lowpan
# echo 'psm 62' > /sys/kernel/debug/bluetooth/hci0/6lowpan
# hcitool lecc E0:06:E6:B7:2A:73
# echo 'connect E0:06:E6:B7:2A:73 1' > \
/sys/kernel/debug/bluetooth/hci0/6lowpan
The 6LoWPAN functionality can be controlled by psm value. If it
is left to 0, then the module is disabled and all the 6LoWPAN
connections are dropped if there were any.
The 6lowpan controlling interface is a temporary solution
until the specifications are ready.
Signed-off-by: Jukka Rissanen <[email protected]>
---
net/bluetooth/6lowpan.c | 13 +++++++++++--
net/bluetooth/Kconfig | 6 +++---
net/bluetooth/Makefile | 4 +++-
3 files changed, 17 insertions(+), 6 deletions(-)
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
index 46cc298..04e0501 100644
--- a/net/bluetooth/6lowpan.c
+++ b/net/bluetooth/6lowpan.c
@@ -14,6 +14,7 @@
#include <linux/if_arp.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
+#include <linux/module.h>
#include <linux/debugfs.h>
#include <net/ipv6.h>
@@ -1223,14 +1224,14 @@ static struct hci_cb lowpan_cb = {
.connect_cfm = lowpan_connect_hcon,
};
-int bt_6lowpan_init(void)
+static int __init bt_6lowpan_init(void)
{
hci_register_cb(&lowpan_cb);
return register_netdevice_notifier(&bt_6lowpan_dev_notifier);
}
-void bt_6lowpan_cleanup(void)
+static void __exit bt_6lowpan_cleanup(void)
{
debugfs_remove(lowpan_debugfs);
@@ -1238,3 +1239,11 @@ void bt_6lowpan_cleanup(void)
unregister_netdevice_notifier(&bt_6lowpan_dev_notifier);
}
+
+module_init(bt_6lowpan_init);
+module_exit(bt_6lowpan_cleanup);
+
+MODULE_AUTHOR("Jukka Rissanen <[email protected]>");
+MODULE_DESCRIPTION("Bluetooth LE 6LoWPAN");
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig
index 06ec144..7ef1a5a 100644
--- a/net/bluetooth/Kconfig
+++ b/net/bluetooth/Kconfig
@@ -6,7 +6,6 @@ menuconfig BT
tristate "Bluetooth subsystem support"
depends on NET && !S390
depends on RFKILL || !RFKILL
- select 6LOWPAN_IPHC if BT_6LOWPAN
select CRC16
select CRYPTO
select CRYPTO_BLKCIPHER
@@ -41,10 +40,11 @@ menuconfig BT
more information, see <http://www.bluez.org/>.
config BT_6LOWPAN
- bool "Bluetooth 6LoWPAN support"
+ tristate "Bluetooth LE 6LoWPAN support"
depends on BT && IPV6
+ select 6LOWPAN_IPHC if BT_6LOWPAN
help
- IPv6 compression over Bluetooth.
+ IPv6 compression over Bluetooth LE.
source "net/bluetooth/rfcomm/Kconfig"
diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile
index ca51246..886e9aa 100644
--- a/net/bluetooth/Makefile
+++ b/net/bluetooth/Makefile
@@ -7,10 +7,12 @@ obj-$(CONFIG_BT_RFCOMM) += rfcomm/
obj-$(CONFIG_BT_BNEP) += bnep/
obj-$(CONFIG_BT_CMTP) += cmtp/
obj-$(CONFIG_BT_HIDP) += hidp/
+obj-$(CONFIG_BT_6LOWPAN) += bluetooth_6lowpan.o
+
+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
-bluetooth-$(CONFIG_BT_6LOWPAN) += 6lowpan.o
subdir-ccflags-y += -D__CHECK_ENDIAN__
--
1.8.3.1
Signed-off-by: Jukka Rissanen <[email protected]>
---
net/bluetooth/l2cap_core.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 60433c4..3a7f560 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -471,8 +471,14 @@ void l2cap_chan_set_defaults(struct l2cap_chan *chan)
chan->max_tx = L2CAP_DEFAULT_MAX_TX;
chan->tx_win = L2CAP_DEFAULT_TX_WINDOW;
chan->tx_win_max = L2CAP_DEFAULT_TX_WINDOW;
+ chan->remote_max_tx = chan->max_tx;
+ chan->remote_tx_win = chan->tx_win;
chan->ack_win = L2CAP_DEFAULT_TX_WINDOW;
chan->sec_level = BT_SECURITY_LOW;
+ chan->flush_to = L2CAP_DEFAULT_FLUSH_TO;
+ chan->retrans_timeout = L2CAP_DEFAULT_RETRANS_TO;
+ chan->monitor_timeout = L2CAP_DEFAULT_MONITOR_TO;
+ chan->conf_state = 0;
set_bit(FLAG_FORCE_ACTIVE, &chan->flags);
}
--
1.8.3.1
Signed-off-by: Jukka Rissanen <[email protected]>
---
include/net/bluetooth/hci_core.h | 26 ++++++++++++++++++++++++++
net/bluetooth/hci_event.c | 3 +++
2 files changed, 29 insertions(+)
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 16a07a1..691a6d0 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -1053,6 +1053,8 @@ struct hci_cb {
void (*create_cfm) (struct hci_dev *hdev);
void (*destroy_cfm) (struct hci_dev *hdev);
+ void (*connect_cfm) (struct hci_conn *conn);
+ void (*disconnect_cfm) (struct hci_conn *conn);
};
static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status)
@@ -1145,6 +1147,30 @@ static inline void hci_destroyed_cfm(struct hci_dev *hdev)
read_unlock(&hci_cb_list_lock);
}
+static inline void hci_connect_cfm(struct hci_conn *hcon)
+{
+ struct hci_cb *cb;
+
+ read_lock(&hci_cb_list_lock);
+ list_for_each_entry(cb, &hci_cb_list, list) {
+ if (cb->connect_cfm)
+ cb->connect_cfm(hcon);
+ }
+ read_unlock(&hci_cb_list_lock);
+}
+
+static inline void hci_disconnect_cfm(struct hci_conn *hcon)
+{
+ struct hci_cb *cb;
+
+ read_lock(&hci_cb_list_lock);
+ list_for_each_entry(cb, &hci_cb_list, list) {
+ if (cb->disconnect_cfm)
+ cb->disconnect_cfm(hcon);
+ }
+ read_unlock(&hci_cb_list_lock);
+}
+
static inline bool eir_has_data_type(u8 *data, size_t data_len, u8 type)
{
size_t parsed = 0;
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 3454807..b1bfa3f 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -2176,6 +2176,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_disconnect_cfm(conn);
hci_conn_del(conn);
/* Re-enable advertising if necessary, since it might
@@ -4051,6 +4052,8 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_pend_le_conn_del(hdev, &conn->dst, conn->dst_type);
+ hci_connect_cfm(conn);
+
unlock:
hci_dev_unlock(hdev);
}
--
1.8.3.1
The l2cap_chan_send() is changed to use kernel memory directly,
so this function must read the user buffer before sending the
message.
The change is done as the 6LoWPAN also uses l2cap_chan_send()
and in order to minimize the amount of code changes, we must
copy the user buffer in sock handling code.
Signed-off-by: Jukka Rissanen <[email protected]>
---
include/net/bluetooth/l2cap.h | 4 +--
net/bluetooth/a2mp.c | 12 +------
net/bluetooth/l2cap_core.c | 76 ++++++++++++++++++++++++-------------------
net/bluetooth/l2cap_sock.c | 14 +++++++-
4 files changed, 58 insertions(+), 48 deletions(-)
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index 4abdcb2..3980b81 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -872,8 +872,8 @@ struct l2cap_chan *l2cap_chan_create(void);
void l2cap_chan_close(struct l2cap_chan *chan, int reason);
int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
bdaddr_t *dst, u8 dst_type);
-int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
- u32 priority);
+int l2cap_chan_send(struct l2cap_chan *chan, unsigned char *msg, size_t len,
+ u32 priority, unsigned int flags);
void l2cap_chan_busy(struct l2cap_chan *chan, int busy);
int l2cap_chan_check_security(struct l2cap_chan *chan);
void l2cap_chan_set_defaults(struct l2cap_chan *chan);
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 9514cc9..9efcda8 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -48,22 +48,12 @@ 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;
u16 total_len = len + sizeof(*cmd);
- struct kvec iv;
- struct msghdr msg;
cmd = __a2mp_build(code, ident, len, data);
if (!cmd)
return;
- iv.iov_base = cmd;
- iv.iov_len = total_len;
-
- memset(&msg, 0, sizeof(msg));
-
- msg.msg_iov = (struct iovec *) &iv;
- msg.msg_iovlen = 1;
-
- l2cap_chan_send(chan, &msg, total_len, 0);
+ l2cap_chan_send(chan, (unsigned char *)cmd, total_len, 0, 0);
kfree(cmd);
}
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index a1e5bb7..60433c4 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -2098,19 +2098,20 @@ static void l2cap_send_ack(struct l2cap_chan *chan)
}
}
-static inline int l2cap_skbuff_fromiovec(struct l2cap_chan *chan,
- struct msghdr *msg, int len,
- int count, struct sk_buff *skb)
+static inline int l2cap_skbuff(struct l2cap_chan *chan,
+ unsigned char *msg, int len,
+ unsigned int flags, int count,
+ struct sk_buff *skb)
{
struct l2cap_conn *conn = chan->conn;
struct sk_buff **frag;
int sent = 0;
- if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count))
- return -EFAULT;
+ memcpy(skb_put(skb, count), msg, count);
sent += count;
len -= count;
+ msg += count;
/* Continuation fragments (no L2CAP header) */
frag = &skb_shinfo(skb)->frag_list;
@@ -2120,19 +2121,19 @@ static inline int l2cap_skbuff_fromiovec(struct l2cap_chan *chan,
count = min_t(unsigned int, conn->mtu, len);
tmp = chan->ops->alloc_skb(chan, count,
- msg->msg_flags & MSG_DONTWAIT);
+ flags & MSG_DONTWAIT);
if (IS_ERR(tmp))
return PTR_ERR(tmp);
*frag = tmp;
- if (memcpy_fromiovec(skb_put(*frag, count), msg->msg_iov, count))
- return -EFAULT;
+ memcpy(skb_put(*frag, count), msg, count);
(*frag)->priority = skb->priority;
sent += count;
len -= count;
+ msg += count;
skb->len += (*frag)->len;
skb->data_len += (*frag)->len;
@@ -2144,8 +2145,8 @@ static inline int l2cap_skbuff_fromiovec(struct l2cap_chan *chan,
}
static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan,
- struct msghdr *msg, size_t len,
- u32 priority)
+ unsigned char *msg, size_t len,
+ u32 priority, unsigned int flags)
{
struct l2cap_conn *conn = chan->conn;
struct sk_buff *skb;
@@ -2158,7 +2159,7 @@ static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan,
count = min_t(unsigned int, (conn->mtu - hlen), len);
skb = chan->ops->alloc_skb(chan, count + hlen,
- msg->msg_flags & MSG_DONTWAIT);
+ flags & MSG_DONTWAIT);
if (IS_ERR(skb))
return skb;
@@ -2170,7 +2171,7 @@ static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan,
lh->len = cpu_to_le16(len + L2CAP_PSMLEN_SIZE);
put_unaligned(chan->psm, (__le16 *) skb_put(skb, L2CAP_PSMLEN_SIZE));
- err = l2cap_skbuff_fromiovec(chan, msg, len, count, skb);
+ err = l2cap_skbuff(chan, msg, len, flags, count, skb);
if (unlikely(err < 0)) {
kfree_skb(skb);
return ERR_PTR(err);
@@ -2179,8 +2180,8 @@ static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan,
}
static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan,
- struct msghdr *msg, size_t len,
- u32 priority)
+ unsigned char *msg, size_t len,
+ u32 priority, unsigned int flags)
{
struct l2cap_conn *conn = chan->conn;
struct sk_buff *skb;
@@ -2192,7 +2193,7 @@ static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan,
count = min_t(unsigned int, (conn->mtu - L2CAP_HDR_SIZE), len);
skb = chan->ops->alloc_skb(chan, count + L2CAP_HDR_SIZE,
- msg->msg_flags & MSG_DONTWAIT);
+ flags & MSG_DONTWAIT);
if (IS_ERR(skb))
return skb;
@@ -2203,7 +2204,7 @@ static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan,
lh->cid = cpu_to_le16(chan->dcid);
lh->len = cpu_to_le16(len);
- err = l2cap_skbuff_fromiovec(chan, msg, len, count, skb);
+ err = l2cap_skbuff(chan, msg, len, flags, count, skb);
if (unlikely(err < 0)) {
kfree_skb(skb);
return ERR_PTR(err);
@@ -2212,8 +2213,8 @@ static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan,
}
static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan,
- struct msghdr *msg, size_t len,
- u16 sdulen)
+ unsigned char *msg, size_t len,
+ u16 sdulen, unsigned int flags)
{
struct l2cap_conn *conn = chan->conn;
struct sk_buff *skb;
@@ -2236,7 +2237,7 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan,
count = min_t(unsigned int, (conn->mtu - hlen), len);
skb = chan->ops->alloc_skb(chan, count + hlen,
- msg->msg_flags & MSG_DONTWAIT);
+ flags & MSG_DONTWAIT);
if (IS_ERR(skb))
return skb;
@@ -2254,7 +2255,7 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan,
if (sdulen)
put_unaligned_le16(sdulen, skb_put(skb, L2CAP_SDULEN_SIZE));
- err = l2cap_skbuff_fromiovec(chan, msg, len, count, skb);
+ err = l2cap_skbuff(chan, msg, len, flags, count, skb);
if (unlikely(err < 0)) {
kfree_skb(skb);
return ERR_PTR(err);
@@ -2267,7 +2268,8 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan,
static int l2cap_segment_sdu(struct l2cap_chan *chan,
struct sk_buff_head *seg_queue,
- struct msghdr *msg, size_t len)
+ unsigned char *msg, size_t len,
+ unsigned int flags)
{
struct sk_buff *skb;
u16 sdu_len;
@@ -2308,7 +2310,8 @@ static int l2cap_segment_sdu(struct l2cap_chan *chan,
}
while (len > 0) {
- skb = l2cap_create_iframe_pdu(chan, msg, pdu_len, sdu_len);
+ skb = l2cap_create_iframe_pdu(chan, msg, pdu_len, sdu_len,
+ flags);
if (IS_ERR(skb)) {
__skb_queue_purge(seg_queue);
@@ -2336,8 +2339,9 @@ static int l2cap_segment_sdu(struct l2cap_chan *chan,
}
static struct sk_buff *l2cap_create_le_flowctl_pdu(struct l2cap_chan *chan,
- struct msghdr *msg,
- size_t len, u16 sdulen)
+ unsigned char *msg,
+ size_t len, u16 sdulen,
+ unsigned int flags)
{
struct l2cap_conn *conn = chan->conn;
struct sk_buff *skb;
@@ -2357,7 +2361,7 @@ static struct sk_buff *l2cap_create_le_flowctl_pdu(struct l2cap_chan *chan,
count = min_t(unsigned int, (conn->mtu - hlen), len);
skb = chan->ops->alloc_skb(chan, count + hlen,
- msg->msg_flags & MSG_DONTWAIT);
+ flags & MSG_DONTWAIT);
if (IS_ERR(skb))
return skb;
@@ -2369,7 +2373,7 @@ static struct sk_buff *l2cap_create_le_flowctl_pdu(struct l2cap_chan *chan,
if (sdulen)
put_unaligned_le16(sdulen, skb_put(skb, L2CAP_SDULEN_SIZE));
- err = l2cap_skbuff_fromiovec(chan, msg, len, count, skb);
+ err = l2cap_skbuff(chan, msg, len, flags, count, skb);
if (unlikely(err < 0)) {
kfree_skb(skb);
return ERR_PTR(err);
@@ -2380,7 +2384,8 @@ static struct sk_buff *l2cap_create_le_flowctl_pdu(struct l2cap_chan *chan,
static int l2cap_segment_le_sdu(struct l2cap_chan *chan,
struct sk_buff_head *seg_queue,
- struct msghdr *msg, size_t len)
+ unsigned char *msg, size_t len,
+ unsigned int flags)
{
struct sk_buff *skb;
size_t pdu_len;
@@ -2399,7 +2404,8 @@ static int l2cap_segment_le_sdu(struct l2cap_chan *chan,
if (len <= pdu_len)
pdu_len = len;
- skb = l2cap_create_le_flowctl_pdu(chan, msg, pdu_len, sdu_len);
+ skb = l2cap_create_le_flowctl_pdu(chan, msg, pdu_len, sdu_len,
+ flags);
if (IS_ERR(skb)) {
__skb_queue_purge(seg_queue);
return PTR_ERR(skb);
@@ -2408,6 +2414,7 @@ static int l2cap_segment_le_sdu(struct l2cap_chan *chan,
__skb_queue_tail(seg_queue, skb);
len -= pdu_len;
+ msg += pdu_len;
if (sdu_len) {
sdu_len = 0;
@@ -2418,8 +2425,8 @@ static int l2cap_segment_le_sdu(struct l2cap_chan *chan,
return 0;
}
-int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
- u32 priority)
+int l2cap_chan_send(struct l2cap_chan *chan, unsigned char *msg, size_t len,
+ u32 priority, unsigned int flags)
{
struct sk_buff *skb;
int err;
@@ -2430,7 +2437,8 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
/* Connectionless channel */
if (chan->chan_type == L2CAP_CHAN_CONN_LESS) {
- skb = l2cap_create_connless_pdu(chan, msg, len, priority);
+ skb = l2cap_create_connless_pdu(chan, msg, len, priority,
+ flags);
if (IS_ERR(skb))
return PTR_ERR(skb);
@@ -2457,7 +2465,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
__skb_queue_head_init(&seg_queue);
- err = l2cap_segment_le_sdu(chan, &seg_queue, msg, len);
+ err = l2cap_segment_le_sdu(chan, &seg_queue, msg, len, flags);
if (chan->state != BT_CONNECTED) {
__skb_queue_purge(&seg_queue);
@@ -2487,7 +2495,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
return -EMSGSIZE;
/* Create a basic PDU */
- skb = l2cap_create_basic_pdu(chan, msg, len, priority);
+ skb = l2cap_create_basic_pdu(chan, msg, len, priority, flags);
if (IS_ERR(skb))
return PTR_ERR(skb);
@@ -2517,7 +2525,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
* since it's possible to block while waiting for memory
* allocation.
*/
- err = l2cap_segment_sdu(chan, &seg_queue, msg, len);
+ err = l2cap_segment_sdu(chan, &seg_queue, msg, len, flags);
/* The channel could have been closed while segmenting,
* check that it is still connected.
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index f59e00c..7e7b28a 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -948,6 +948,7 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
{
struct sock *sk = sock->sk;
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
+ unsigned char *buf;
int err;
BT_DBG("sock %p, sk %p", sock, sk);
@@ -968,10 +969,21 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
if (err)
return err;
+ buf = kmalloc(len, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ if (memcpy_fromiovec(buf, msg->msg_iov, len)) {
+ err = -EFAULT;
+ goto done;
+ }
+
l2cap_chan_lock(chan);
- err = l2cap_chan_send(chan, msg, len, sk->sk_priority);
+ err = l2cap_chan_send(chan, buf, len, sk->sk_priority, msg->msg_flags);
l2cap_chan_unlock(chan);
+done:
+ kfree(buf);
return err;
}
--
1.8.3.1
Signed-off-by: Jukka Rissanen <[email protected]>
---
include/net/bluetooth/hci_core.h | 27 +++++++++++++++++++++++++++
net/bluetooth/hci_core.c | 17 +++++++++++++++++
2 files changed, 44 insertions(+)
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index b386bf1..16a07a1 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -1050,6 +1050,9 @@ struct hci_cb {
__u8 encrypt);
void (*key_change_cfm) (struct hci_conn *conn, __u8 status);
void (*role_switch_cfm) (struct hci_conn *conn, __u8 status, __u8 role);
+
+ void (*create_cfm) (struct hci_dev *hdev);
+ void (*destroy_cfm) (struct hci_dev *hdev);
};
static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status)
@@ -1118,6 +1121,30 @@ static inline void hci_role_switch_cfm(struct hci_conn *conn, __u8 status,
read_unlock(&hci_cb_list_lock);
}
+static inline void hci_created_cfm(struct hci_dev *hdev)
+{
+ struct hci_cb *cb;
+
+ read_lock(&hci_cb_list_lock);
+ list_for_each_entry(cb, &hci_cb_list, list) {
+ if (cb->create_cfm)
+ cb->create_cfm(hdev);
+ }
+ read_unlock(&hci_cb_list_lock);
+}
+
+static inline void hci_destroyed_cfm(struct hci_dev *hdev)
+{
+ struct hci_cb *cb;
+
+ read_lock(&hci_cb_list_lock);
+ list_for_each_entry(cb, &hci_cb_list, list) {
+ if (cb->destroy_cfm)
+ cb->destroy_cfm(hdev);
+ }
+ read_unlock(&hci_cb_list_lock);
+}
+
static inline bool eir_has_data_type(u8 *data, size_t data_len, u8 type)
{
size_t parsed = 0;
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 0a43cce..a5d6698 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -3996,6 +3996,8 @@ int hci_register_dev(struct hci_dev *hdev)
queue_work(hdev->req_workqueue, &hdev->power_on);
+ hci_created_cfm(hdev);
+
return id;
err_tfm:
@@ -4017,6 +4019,8 @@ void hci_unregister_dev(struct hci_dev *hdev)
BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus);
+ hci_destroyed_cfm(hdev);
+
set_bit(HCI_UNREGISTER, &hdev->dev_flags);
id = hdev->id;
@@ -4289,6 +4293,19 @@ int hci_register_cb(struct hci_cb *cb)
list_add(&cb->list, &hci_cb_list);
write_unlock(&hci_cb_list_lock);
+ /* Call created callback so that possible module will get
+ * information about the hci devices.
+ */
+ if (cb->create_cfm) {
+ struct hci_dev *hdev;
+
+ read_lock(&hci_dev_list_lock);
+ list_for_each_entry(hdev, &hci_dev_list, list) {
+ cb->create_cfm(hdev);
+ }
+ read_unlock(&hci_dev_list_lock);
+ }
+
return 0;
}
EXPORT_SYMBOL(hci_register_cb);
--
1.8.3.1