Return-Path: Content-Type: text/plain; charset=windows-1252 Mime-Version: 1.0 (Mac OS X Mail 6.6 \(1510\)) Subject: Re: [RFC 1/5] Bluetooth: Initial skeleton code for BT LE 6LoWPAN From: Marcel Holtmann In-Reply-To: <1382511164-1989-2-git-send-email-jukka.rissanen@linux.intel.com> Date: Wed, 23 Oct 2013 13:58:22 +0200 Cc: linux-bluetooth@vger.kernel.org Message-Id: References: <1382511164-1989-1-git-send-email-jukka.rissanen@linux.intel.com> <1382511164-1989-2-git-send-email-jukka.rissanen@linux.intel.com> To: Jukka Rissanen Sender: linux-bluetooth-owner@vger.kernel.org List-ID: Hi Jukka, > include/net/bluetooth/l2cap.h | 1 + > net/bluetooth/Makefile | 2 +- > net/bluetooth/ble_6lowpan.c | 404 ++++++++++++++++++++++++++++++++++++++++++ > net/bluetooth/ble_6lowpan.h | 27 +++ > net/bluetooth/l2cap_core.c | 22 ++- > 5 files changed, 454 insertions(+), 2 deletions(-) > create mode 100644 net/bluetooth/ble_6lowpan.c > create mode 100644 net/bluetooth/ble_6lowpan.h > > diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h > index 3d922b9..645cd30 100644 > --- a/include/net/bluetooth/l2cap.h > +++ b/include/net/bluetooth/l2cap.h > @@ -133,6 +133,7 @@ struct l2cap_conninfo { > #define L2CAP_FC_L2CAP 0x02 > #define L2CAP_FC_CONNLESS 0x04 > #define L2CAP_FC_A2MP 0x08 > +#define L2CAP_FC_6LOWPAN 0x3e > > /* L2CAP Control Field bit masks */ > #define L2CAP_CTRL_SAR 0xC000 > diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile > index 6a791e7..a70625b 100644 > --- a/net/bluetooth/Makefile > +++ b/net/bluetooth/Makefile > @@ -10,6 +10,6 @@ obj-$(CONFIG_BT_HIDP) += hidp/ > > bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \ > hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o \ > - a2mp.o amp.o > + a2mp.o amp.o ble_6lowpan.o we tried to not use ble prefix. I would just name it 6lowpan.c here. > subdir-ccflags-y += -D__CHECK_ENDIAN__ > diff --git a/net/bluetooth/ble_6lowpan.c b/net/bluetooth/ble_6lowpan.c > new file mode 100644 > index 0000000..0fd3302 > --- /dev/null > +++ b/net/bluetooth/ble_6lowpan.c > @@ -0,0 +1,404 @@ > +/* > + Copyright (c) 2013 Intel Corp. > + > + This program is free software; you can redistribute it and/or modify > + it under the terms of the GNU General Public License version 2 and > + only version 2 as published by the Free Software Foundation. > + > + This program is distributed in the hope that it will be useful, > + but WITHOUT ANY WARRANTY; without even the implied warranty of > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + GNU General Public License for more details. > +*/ > + > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > +#include > + > +#include > + > +#include > +#include > +#include > + > +#include "../ieee802154/6lowpan.h" /* for the compression defines */ > + > +#define IFACE_NAME_TEMPLATE "ble6lowpan%d" Wonder what would be a good template name here. Maybe just calling them bt%d is good enough. At the end of the day nobody cares that this is 6loWPAN. We just want IPv6 over Bluetooth. > + > +/* > + * The devices list contains those devices that we are acting > + * as a proxy. The BT LE 6LoWPAN device is a virtual device that > + * connects to the Bluetooth LE device. The real connection to > + * BT LE device is done via l2cap layer. There exists one > + * virtual device / one BT LE 6LoWPAN device. The list contains > + * struct ble_6lowpan_dev_record elements. > + */ > +static LIST_HEAD(ble_6lowpan_devices); > +DEFINE_RWLOCK(net_dev_list_lock); > + > +struct ble_6lowpan_dev_record { > + struct net_device *dev; > + struct delayed_work delete_timer; > + struct list_head list; > +}; > + > +struct ble_6lowpan_dev_info { > + struct net_device *net; > + struct l2cap_conn *conn; > + uint16_t ifindex; > + bdaddr_t myaddr; > + > + /* peer addresses in various formats */ > + bdaddr_t addr; > + unsigned char ieee802154_addr[IEEE802154_ADDR_LEN]; > + struct in6_addr peer; > +}; Instead of ble_ use bt_ as prefix. > + > +struct lowpan_fragment { > + struct sk_buff *skb; /* skb to be assembled */ > + u16 length; /* length to be assemled */ > + u32 bytes_rcv; /* bytes received */ > + u16 tag; /* current fragment tag */ > + struct timer_list timer; /* assembling timer */ > + struct list_head list; /* fragments list */ > +}; > + > +#define DELETE_TIMEOUT msecs_to_jiffies(1) > + > +/* TTL uncompression values */ > +static const u8 lowpan_ttl_values[] = {0, 1, 64, 255}; > + > +static inline struct > +ble_6lowpan_dev_info *ble_6lowpan_dev_info(const struct net_device *dev) > +{ > + return netdev_priv(dev); > +} > + > +/* print data in line */ > +static inline void ble_6lowpan_raw_dump_inline(const char *caller, char *msg, > + unsigned char *buf, int len) > +{ > +#ifdef DEBUG > + if (msg) > + pr_debug("%s():%s: ", caller, msg); > + print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_NONE, > + 16, 1, buf, len, false); > +#endif /* DEBUG */ > +} > + > +/* > + * print data in a table format: > + * > + * addr: xx xx xx xx xx xx > + * addr: xx xx xx xx xx xx > + * ... > + */ > +static inline void ble_6lowpan_raw_dump_table(const char *caller, char *msg, > + unsigned char *buf, int len) > +{ > +#ifdef DEBUG > + if (msg) > + pr_debug("%s():%s:\n", caller, msg); > + print_hex_dump(KERN_DEBUG, "\t", DUMP_PREFIX_OFFSET, > + 16, 1, buf, len, false); > +#endif /* DEBUG */ > +} > + > +static int ble_6lowpan_recv_pkt(struct sk_buff *skb, struct net_device *dev) > +{ > + kfree_skb(skb); > + return NET_RX_DROP; > +} > + > +/* Packet from BT LE device */ > +int ble_6lowpan_recv(struct l2cap_conn *conn, struct sk_buff *skb) > +{ > + struct ble_6lowpan_dev_record *entry, *tmp; > + struct net_device *dev = NULL; > + int status = -ENOENT; > + > + write_lock(&net_dev_list_lock); > + > + list_for_each_entry_safe(entry, tmp, &ble_6lowpan_devices, list) { > + if (ble_6lowpan_dev_info(entry->dev)->conn == conn) { > + dev = ble_6lowpan_dev_info(entry->dev)->net; > + break; > + } > + } > + > + write_unlock(&net_dev_list_lock); > + > + if (dev) { > + status = ble_6lowpan_recv_pkt(skb, dev); > + BT_DBG("recv pkt %d", status); > + } > + > + return status; > +} > + > +static void ble_6lowpan_do_send(struct l2cap_conn *conn, struct sk_buff *skb) > +{ > + BT_DBG("conn %p, skb %p len %d priority %u", conn, skb, skb->len, > + skb->priority); > + > + return; > +} > + > +static int lowpan_conn_send(struct l2cap_conn *conn, > + void *msg, size_t len, u32 priority, > + struct net_device *dev) > +{ > + struct sk_buff *skb = {0}; > + > + ble_6lowpan_do_send(conn, skb); > + return 0; > +} > + > +/* Packet to BT LE device */ > +static int ble_6lowpan_send(struct l2cap_conn *conn, const void *saddr, > + const void *daddr, struct sk_buff *skb, > + struct net_device *dev) > +{ > + ble_6lowpan_raw_dump_table(__func__, > + "raw skb data dump before fragmentation", > + skb->data, skb->len); > + > + return lowpan_conn_send(conn, skb->data, skb->len, 0, dev); > +} > + > +static netdev_tx_t ble_6lowpan_xmit(struct sk_buff *skb, struct net_device *dev) > +{ > + int err = -1; > + > + pr_debug("ble 6lowpan packet xmit\n"); > + > + if (ble_6lowpan_dev_info(dev)->conn) > + err = ble_6lowpan_send(ble_6lowpan_dev_info(dev)->conn, > + dev->dev_addr, > + &ble_6lowpan_dev_info(dev)->ieee802154_addr, > + skb, > + dev); > + else > + BT_DBG("ERROR: no BT LE 6LoWPAN device found"); > + > + dev_kfree_skb(skb); > + > + if (err) > + BT_DBG("ERROR: xmit failed (%d)", err); > + > + return (err < 0) ? NET_XMIT_DROP : err; > +} > + > +static const struct net_device_ops ble_6lowpan_netdev_ops = { > + .ndo_start_xmit = ble_6lowpan_xmit, > +}; > + > +static void ble_6lowpan_setup(struct net_device *dev) > +{ > + dev->addr_len = IEEE802154_ADDR_LEN; > + dev->type = ARPHRD_IEEE802154; Seems like we need to ask for a new ARP header type included in include/linux/uapi/if_arp.h. I wonder if this should be a generic one for 6loWPAN or a Bluetooth specific one. Maybe it is a good idea to prepare a patch for netdev mailing list and see what these people thing. > + > + dev->hard_header_len = 0; > + dev->needed_tailroom = 0; > + dev->mtu = IPV6_MIN_MTU; > + dev->tx_queue_len = 0; > + dev->flags = IFF_RUNNING | IFF_POINTOPOINT; > + dev->watchdog_timeo = 0; > + > + dev->netdev_ops = &ble_6lowpan_netdev_ops; > + dev->destructor = free_netdev; > +} > + > +static struct device_type ble_type = { > + .name = "ble6lowpan", > +}; > + > +static void set_addr(u8 *eui, u8 *addr) > +{ > + /* addr is the BT address in little-endian format */ > + eui[0] = addr[5]; > + eui[1] = addr[4]; > + eui[2] = addr[3]; > + eui[3] = 0xFF; > + eui[4] = 0xFE; > + eui[5] = addr[2]; > + eui[6] = addr[1]; > + eui[7] = addr[0]; > + > + eui[0] ^= 2; > +} > + > +static void set_dev_addr(struct net_device *net, bdaddr_t *addr) > +{ > + net->addr_assign_type = NET_ADDR_PERM; > + set_addr(net->dev_addr, addr->b); > + net->dev_addr[0] ^= 2; > +} So now it gets a bit tricky. LE has public and random addresses and we do now support controller having both. We also support controllers that have no public address. So question is what do we do when a controller only has a random address. > + > +static void ifup(struct net_device *net) > +{ > + int status; > + > + rtnl_lock(); > + if ((status = dev_open(net)) < 0) > + BT_INFO("iface %s cannot be opened (%d)", net->name, > + status); > + rtnl_unlock(); > +} > + > +/* > + * This gets called when BT LE 6LoWPan device is connected. We then > + * create network device that acts as a proxy between BT LE device > + * and kernel network stack. > + */ > +int ble_6lowpan_add_conn(struct l2cap_conn *conn) > +{ > + struct net_device *net; > + struct ble_6lowpan_dev_info *dev; > + struct ble_6lowpan_dev_record *entry; > + int status; > + > + net = alloc_netdev(sizeof(struct ble_6lowpan_dev_info), > + IFACE_NAME_TEMPLATE, ble_6lowpan_setup); > + if (!net) > + return -ENOMEM; > + > + dev = netdev_priv(net); > + dev->net = net; > + > + memcpy(&dev->myaddr, &conn->hcon->hdev->bdaddr, sizeof(bdaddr_t)); > + memcpy(&dev->addr, &conn->hcon->dst, sizeof(bdaddr_t)); You need to use hcon->src and hcon->src_type for the source address. hdev->bdaddr is always the public address, but we not always use that. I fixed this lately to make sure that hcon->src always gives you the address that was really used when establishing the connection. There is also a hcon->dst_type for the remote address since that can be easily a random address as well. Check own_address_type in debugfs if you want to play with using a random address. You need to first use the mgmt command set static address to program one before powering on the controller. > + > + set_dev_addr(net, &dev->myaddr); > + > + dev->conn = conn; > + > + net->netdev_ops = &ble_6lowpan_netdev_ops; > + SET_NETDEV_DEV(net, &conn->hcon->dev); > + SET_NETDEV_DEVTYPE(net, &ble_type); I am not sure this should be some other devtype. It should clearly not be empty since that would be confusing ConnMan. However I get the feeling this should stay as bluetooth devtype like we do for BNEP. We can use the ARP header type to see if this is an Ethernet emulation or native IP. This reminds me, we tried to introduce a raw IP ARP header type for WiMAX. Not sure if that ever went upstream. In the end we decided for Ethernet emulation for WiMAX. For IPv6 over LE, the Ethernet emulation is clearly not an option. > + > + status = register_netdev(net); > + if (status < 0) { > + BT_INFO("register_netdev failed %d", status); > + free_netdev(net); > + return status; > + } else { I would just not bother with an else part here. You already left the function with return. So you can just continue. > + struct inet6_dev *idev; > + > + BT_DBG("ifindex %d peer bdaddr %pMR my addr %pMR", > + net->ifindex, &dev->addr, &dev->myaddr); > + dev->ifindex = net->ifindex; > + set_bit(__LINK_STATE_PRESENT, &net->state); > + > + idev = in6_dev_get(net); > + if (idev) { > + idev->cnf.autoconf = 1; > + idev->cnf.forwarding = 1; > + idev->cnf.accept_ra = 2; > + > + in6_dev_put(idev); > + } > + > + entry = kzalloc(sizeof(struct ble_6lowpan_dev_record), > + GFP_KERNEL); > + if (!entry) > + return -ENOMEM; > + > + entry->dev = net; > + > + write_lock(&net_dev_list_lock); > + INIT_LIST_HEAD(&entry->list); > + list_add(&entry->list, &ble_6lowpan_devices); > + write_unlock(&net_dev_list_lock); > + > + ifup(net); > + } > + > + return 0; > +} > + > +static void delete_timeout(struct work_struct *work) > +{ > + struct ble_6lowpan_dev_record *entry = container_of(work, > + struct ble_6lowpan_dev_record, > + delete_timer.work); You need to find a shorter struct name ;) > + > + unregister_netdev(entry->dev); > + kfree(entry); > +} > + > +int ble_6lowpan_del_conn(struct l2cap_conn *conn) > +{ > + struct ble_6lowpan_dev_record *entry, *tmp; > + int status = -ENOENT; > + > + write_lock(&net_dev_list_lock); > + > + list_for_each_entry_safe(entry, tmp, &ble_6lowpan_devices, list) { > + if (ble_6lowpan_dev_info(entry->dev)->conn == conn) { > + list_del(&entry->list); > + status = 0; > + break; > + } > + } > + > + write_unlock(&net_dev_list_lock); > + > + if (!status) { > + INIT_DELAYED_WORK(&entry->delete_timer, delete_timeout); > + schedule_delayed_work(&entry->delete_timer, DELETE_TIMEOUT); > + } > + > + return status; > +} > + > +static int ble_6lowpan_device_event(struct notifier_block *unused, > + unsigned long event, void *ptr) > +{ > + struct net_device *dev = netdev_notifier_info_to_dev(ptr); > + struct ble_6lowpan_dev_record *entry, *tmp; > + > + if (dev->type == ARPHRD_IEEE802154) { Just check for the ARP header type we are looking for. If not, then just return. No need for the extra nesting here. > + switch (event) { > + case NETDEV_UNREGISTER: > + write_lock(&net_dev_list_lock); > + list_for_each_entry_safe(entry, tmp, > + &ble_6lowpan_devices, > + list) { > + if (entry->dev == dev) { > + list_del(&entry->list); > + kfree(entry); > + break; > + } > + } > + write_unlock(&net_dev_list_lock); > + break; > + } > + } > + > + return NOTIFY_DONE; > +} > + > +static struct notifier_block ble_6lowpan_dev_notifier = { > + .notifier_call = ble_6lowpan_device_event, > +}; > + > +int ble_6lowpan_init(void) > +{ > + int err; > + > + err = register_netdevice_notifier(&ble_6lowpan_dev_notifier); > + > + return err; > +} return register_netdevice_? > + > +void ble_6lowpan_cleanup(void) > +{ > + unregister_netdevice_notifier(&ble_6lowpan_dev_notifier); > +} > diff --git a/net/bluetooth/ble_6lowpan.h b/net/bluetooth/ble_6lowpan.h > new file mode 100644 > index 0000000..7975a55 > --- /dev/null > +++ b/net/bluetooth/ble_6lowpan.h > @@ -0,0 +1,27 @@ > +/* > + 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 __BLE_LOWPAN_H > +#define __BLE_LOWPAN_H > + > +#include > +#include > + > +int ble_6lowpan_recv(struct l2cap_conn *conn, struct sk_buff *skb); > +int ble_6lowpan_add_conn(struct l2cap_conn *conn); > +int ble_6lowpan_del_conn(struct l2cap_conn *conn); > +int ble_6lowpan_init(void); > +void ble_6lowpan_cleanup(void); > +bool ble_6lowpan_is_enabled(struct hci_dev *hdev, bdaddr_t *dst); > + > +#endif /* __BLE_LOWPAN_H */ > diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c > index d52bd0d..fb1a49c 100644 > --- a/net/bluetooth/l2cap_core.c > +++ b/net/bluetooth/l2cap_core.c > @@ -40,6 +40,7 @@ > #include "smp.h" > #include "a2mp.h" > #include "amp.h" > +#include "ble_6lowpan.h" > > bool disable_ertm; > > @@ -6500,6 +6501,10 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb) > l2cap_conn_del(conn->hcon, EACCES); > break; > > + case L2CAP_FC_6LOWPAN: > + ble_6lowpan_recv(conn, skb); > + break; > + > default: > l2cap_data_channel(conn, cid, skb); > break; > @@ -6537,6 +6542,11 @@ int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr) > return exact ? lm1 : lm2; > } > > +static bool is_ble_6lowpan(void) > +{ > + return false; > +} > + > void l2cap_connect_cfm(struct hci_conn *hcon, u8 status) > { > struct l2cap_conn *conn; > @@ -6545,8 +6555,12 @@ void l2cap_connect_cfm(struct hci_conn *hcon, u8 status) > > if (!status) { > conn = l2cap_conn_add(hcon); > - if (conn) > + if (conn) { > l2cap_conn_ready(conn); > + > + if (hcon->type == LE_LINK && is_ble_6lowpan()) > + ble_6lowpan_add_conn(conn); > + } > } else { > l2cap_conn_del(hcon, bt_to_errno(status)); > } > @@ -6567,6 +6581,9 @@ void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason) > { > BT_DBG("hcon %p reason %d", hcon, reason); > > + if (hcon->type == LE_LINK && is_ble_6lowpan()) > + ble_6lowpan_del_conn(hcon->l2cap_data); > + > l2cap_conn_del(hcon, bt_to_errno(reason)); > } > > @@ -6849,11 +6866,14 @@ int __init l2cap_init(void) > l2cap_debugfs = debugfs_create_file("l2cap", 0444, bt_debugfs, > NULL, &l2cap_debugfs_fops); > > + ble_6lowpan_init(); > + > return 0; > } > > void l2cap_exit(void) > { > + ble_6lowpan_cleanup(); > debugfs_remove(l2cap_debugfs); > l2cap_cleanup_sockets(); > } Regards Marcel