提交 15a892e7 编写于 作者: J John W. Linville

Merge branch 'for-upstream' of...

Merge branch 'for-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next
...@@ -2302,6 +2302,14 @@ F: security/capability.c ...@@ -2302,6 +2302,14 @@ F: security/capability.c
F: security/commoncap.c F: security/commoncap.c
F: kernel/capability.c F: kernel/capability.c
CC2520 IEEE-802.15.4 RADIO DRIVER
M: Varka Bhadram <varkabhadram@gmail.com>
L: linux-wpan@vger.kernel.org
S: Maintained
F: drivers/net/ieee802154/cc2520.c
F: include/linux/spi/cc2520.h
F: Documentation/devicetree/bindings/net/ieee802154/cc2520.txt
CELL BROADBAND ENGINE ARCHITECTURE CELL BROADBAND ENGINE ARCHITECTURE
M: Arnd Bergmann <arnd@arndb.de> M: Arnd Bergmann <arnd@arndb.de>
L: linuxppc-dev@lists.ozlabs.org L: linuxppc-dev@lists.ozlabs.org
...@@ -4689,6 +4697,13 @@ S: Maintained ...@@ -4689,6 +4697,13 @@ S: Maintained
F: net/ieee802154/ F: net/ieee802154/
F: net/mac802154/ F: net/mac802154/
F: drivers/net/ieee802154/ F: drivers/net/ieee802154/
F: include/linux/nl802154.h
F: include/linux/ieee802154.h
F: include/net/nl802154.h
F: include/net/mac802154.h
F: include/net/af_ieee802154.h
F: include/net/cfg802154.h
F: include/net/ieee802154_netdev.h
F: Documentation/networking/ieee802154.txt F: Documentation/networking/ieee802154.txt
IGUANAWORKS USB IR TRANSCEIVER IGUANAWORKS USB IR TRANSCEIVER
......
...@@ -79,6 +79,7 @@ static const struct usb_device_id ath3k_table[] = { ...@@ -79,6 +79,7 @@ static const struct usb_device_id ath3k_table[] = {
{ USB_DEVICE(0x0489, 0xe057) }, { USB_DEVICE(0x0489, 0xe057) },
{ USB_DEVICE(0x0489, 0xe056) }, { USB_DEVICE(0x0489, 0xe056) },
{ USB_DEVICE(0x0489, 0xe05f) }, { USB_DEVICE(0x0489, 0xe05f) },
{ USB_DEVICE(0x0489, 0xe078) },
{ USB_DEVICE(0x04c5, 0x1330) }, { USB_DEVICE(0x04c5, 0x1330) },
{ USB_DEVICE(0x04CA, 0x3004) }, { USB_DEVICE(0x04CA, 0x3004) },
{ USB_DEVICE(0x04CA, 0x3005) }, { USB_DEVICE(0x04CA, 0x3005) },
...@@ -130,6 +131,7 @@ static const struct usb_device_id ath3k_blist_tbl[] = { ...@@ -130,6 +131,7 @@ static const struct usb_device_id ath3k_blist_tbl[] = {
{ USB_DEVICE(0x0489, 0xe056), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0489, 0xe056), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0489, 0xe05f), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0489, 0xe05f), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0489, 0xe078), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04c5, 0x1330), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04c5, 0x1330), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3004), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04ca, 0x3004), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 },
......
...@@ -156,6 +156,7 @@ static const struct usb_device_id blacklist_table[] = { ...@@ -156,6 +156,7 @@ static const struct usb_device_id blacklist_table[] = {
{ USB_DEVICE(0x0489, 0xe056), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0489, 0xe056), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0489, 0xe05f), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0489, 0xe05f), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0489, 0xe078), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04c5, 0x1330), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04c5, 0x1330), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3004), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04ca, 0x3004), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 },
......
...@@ -168,6 +168,36 @@ static void h5_timed_event(unsigned long arg) ...@@ -168,6 +168,36 @@ static void h5_timed_event(unsigned long arg)
hci_uart_tx_wakeup(hu); hci_uart_tx_wakeup(hu);
} }
static void h5_peer_reset(struct hci_uart *hu)
{
struct h5 *h5 = hu->priv;
struct sk_buff *skb;
const unsigned char hard_err[] = { 0x10, 0x01, 0x00 };
BT_ERR("Peer device has reset");
h5->state = H5_UNINITIALIZED;
del_timer(&h5->timer);
skb_queue_purge(&h5->rel);
skb_queue_purge(&h5->unrel);
skb_queue_purge(&h5->unack);
h5->tx_seq = 0;
h5->tx_ack = 0;
skb = bt_skb_alloc(3, GFP_ATOMIC);
if (!skb)
return;
bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
memcpy(skb_put(skb, 3), hard_err, 3);
/* Send Hardware Error to upper stack */
hci_recv_frame(hu->hdev, skb);
}
static int h5_open(struct hci_uart *hu) static int h5_open(struct hci_uart *hu)
{ {
struct h5 *h5; struct h5 *h5;
...@@ -283,8 +313,12 @@ static void h5_handle_internal_rx(struct hci_uart *hu) ...@@ -283,8 +313,12 @@ static void h5_handle_internal_rx(struct hci_uart *hu)
conf_req[2] = h5_cfg_field(h5); conf_req[2] = h5_cfg_field(h5);
if (memcmp(data, sync_req, 2) == 0) { if (memcmp(data, sync_req, 2) == 0) {
if (h5->state == H5_ACTIVE)
h5_peer_reset(hu);
h5_link_control(hu, sync_rsp, 2); h5_link_control(hu, sync_rsp, 2);
} else if (memcmp(data, sync_rsp, 2) == 0) { } else if (memcmp(data, sync_rsp, 2) == 0) {
if (h5->state == H5_ACTIVE)
h5_peer_reset(hu);
h5->state = H5_INITIALIZED; h5->state = H5_INITIALIZED;
h5_link_control(hu, conf_req, 3); h5_link_control(hu, conf_req, 3);
} else if (memcmp(data, conf_req, 2) == 0) { } else if (memcmp(data, conf_req, 2) == 0) {
......
...@@ -10,16 +10,6 @@ menuconfig IEEE802154_DRIVERS ...@@ -10,16 +10,6 @@ menuconfig IEEE802154_DRIVERS
If you say N, all options in this submenu will be skipped and If you say N, all options in this submenu will be skipped and
disabled. disabled.
config IEEE802154_FAKEHARD
tristate "Fake LR-WPAN driver with several interconnected devices"
depends on IEEE802154_DRIVERS
---help---
Say Y here to enable the fake driver that serves as an example
of HardMAC device driver.
This driver can also be built as a module. To do so say M here.
The module will be called 'fakehard'.
config IEEE802154_FAKELB config IEEE802154_FAKELB
depends on IEEE802154_DRIVERS && MAC802154 depends on IEEE802154_DRIVERS && MAC802154
tristate "IEEE 802.15.4 loopback driver" tristate "IEEE 802.15.4 loopback driver"
......
obj-$(CONFIG_IEEE802154_FAKEHARD) += fakehard.o
obj-$(CONFIG_IEEE802154_FAKELB) += fakelb.o obj-$(CONFIG_IEEE802154_FAKELB) += fakelb.o
obj-$(CONFIG_IEEE802154_AT86RF230) += at86rf230.o obj-$(CONFIG_IEEE802154_AT86RF230) += at86rf230.o
obj-$(CONFIG_IEEE802154_MRF24J40) += mrf24j40.o obj-$(CONFIG_IEEE802154_MRF24J40) += mrf24j40.o
......
此差异已折叠。
...@@ -21,10 +21,10 @@ ...@@ -21,10 +21,10 @@
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/pinctrl/consumer.h> #include <linux/pinctrl/consumer.h>
#include <linux/of_gpio.h> #include <linux/of_gpio.h>
#include <linux/ieee802154.h>
#include <net/mac802154.h> #include <net/mac802154.h>
#include <net/wpan-phy.h> #include <net/cfg802154.h>
#include <net/ieee802154.h>
#define SPI_COMMAND_BUFFER 3 #define SPI_COMMAND_BUFFER 3
#define HIGH 1 #define HIGH 1
...@@ -193,7 +193,7 @@ ...@@ -193,7 +193,7 @@
/* Driver private information */ /* Driver private information */
struct cc2520_private { struct cc2520_private {
struct spi_device *spi; /* SPI device structure */ struct spi_device *spi; /* SPI device structure */
struct ieee802154_dev *dev; /* IEEE-802.15.4 device */ struct ieee802154_hw *hw; /* IEEE-802.15.4 device */
u8 *buf; /* SPI TX/Rx data buffer */ u8 *buf; /* SPI TX/Rx data buffer */
struct mutex buffer_mutex; /* SPI buffer mutex */ struct mutex buffer_mutex; /* SPI buffer mutex */
bool is_tx; /* Flag for sync b/w Tx and Rx */ bool is_tx; /* Flag for sync b/w Tx and Rx */
...@@ -453,20 +453,20 @@ cc2520_read_rxfifo(struct cc2520_private *priv, u8 *data, u8 len, u8 *lqi) ...@@ -453,20 +453,20 @@ cc2520_read_rxfifo(struct cc2520_private *priv, u8 *data, u8 len, u8 *lqi)
return status; return status;
} }
static int cc2520_start(struct ieee802154_dev *dev) static int cc2520_start(struct ieee802154_hw *hw)
{ {
return cc2520_cmd_strobe(dev->priv, CC2520_CMD_SRXON); return cc2520_cmd_strobe(hw->priv, CC2520_CMD_SRXON);
} }
static void cc2520_stop(struct ieee802154_dev *dev) static void cc2520_stop(struct ieee802154_hw *hw)
{ {
cc2520_cmd_strobe(dev->priv, CC2520_CMD_SRFOFF); cc2520_cmd_strobe(hw->priv, CC2520_CMD_SRFOFF);
} }
static int static int
cc2520_tx(struct ieee802154_dev *dev, struct sk_buff *skb) cc2520_tx(struct ieee802154_hw *hw, struct sk_buff *skb)
{ {
struct cc2520_private *priv = dev->priv; struct cc2520_private *priv = hw->priv;
unsigned long flags; unsigned long flags;
int rc; int rc;
u8 status = 0; u8 status = 0;
...@@ -524,7 +524,7 @@ static int cc2520_rx(struct cc2520_private *priv) ...@@ -524,7 +524,7 @@ static int cc2520_rx(struct cc2520_private *priv)
if (len < 2 || len > IEEE802154_MTU) if (len < 2 || len > IEEE802154_MTU)
return -EINVAL; return -EINVAL;
skb = alloc_skb(len, GFP_KERNEL); skb = dev_alloc_skb(len);
if (!skb) if (!skb)
return -ENOMEM; return -ENOMEM;
...@@ -536,7 +536,7 @@ static int cc2520_rx(struct cc2520_private *priv) ...@@ -536,7 +536,7 @@ static int cc2520_rx(struct cc2520_private *priv)
skb_trim(skb, skb->len - 2); skb_trim(skb, skb->len - 2);
ieee802154_rx_irqsafe(priv->dev, skb, lqi); ieee802154_rx_irqsafe(priv->hw, skb, lqi);
dev_vdbg(&priv->spi->dev, "RXFIFO: %x %x\n", len, lqi); dev_vdbg(&priv->spi->dev, "RXFIFO: %x %x\n", len, lqi);
...@@ -544,9 +544,9 @@ static int cc2520_rx(struct cc2520_private *priv) ...@@ -544,9 +544,9 @@ static int cc2520_rx(struct cc2520_private *priv)
} }
static int static int
cc2520_ed(struct ieee802154_dev *dev, u8 *level) cc2520_ed(struct ieee802154_hw *hw, u8 *level)
{ {
struct cc2520_private *priv = dev->priv; struct cc2520_private *priv = hw->priv;
u8 status = 0xff; u8 status = 0xff;
u8 rssi; u8 rssi;
int ret; int ret;
...@@ -569,12 +569,11 @@ cc2520_ed(struct ieee802154_dev *dev, u8 *level) ...@@ -569,12 +569,11 @@ cc2520_ed(struct ieee802154_dev *dev, u8 *level)
} }
static int static int
cc2520_set_channel(struct ieee802154_dev *dev, int page, int channel) cc2520_set_channel(struct ieee802154_hw *hw, u8 page, u8 channel)
{ {
struct cc2520_private *priv = dev->priv; struct cc2520_private *priv = hw->priv;
int ret; int ret;
might_sleep();
dev_dbg(&priv->spi->dev, "trying to set channel\n"); dev_dbg(&priv->spi->dev, "trying to set channel\n");
BUG_ON(page != 0); BUG_ON(page != 0);
...@@ -588,12 +587,12 @@ cc2520_set_channel(struct ieee802154_dev *dev, int page, int channel) ...@@ -588,12 +587,12 @@ cc2520_set_channel(struct ieee802154_dev *dev, int page, int channel)
} }
static int static int
cc2520_filter(struct ieee802154_dev *dev, cc2520_filter(struct ieee802154_hw *hw,
struct ieee802154_hw_addr_filt *filt, unsigned long changed) struct ieee802154_hw_addr_filt *filt, unsigned long changed)
{ {
struct cc2520_private *priv = dev->priv; struct cc2520_private *priv = hw->priv;
if (changed & IEEE802515_AFILT_PANID_CHANGED) { if (changed & IEEE802154_AFILT_PANID_CHANGED) {
u16 panid = le16_to_cpu(filt->pan_id); u16 panid = le16_to_cpu(filt->pan_id);
dev_vdbg(&priv->spi->dev, dev_vdbg(&priv->spi->dev,
...@@ -602,7 +601,7 @@ cc2520_filter(struct ieee802154_dev *dev, ...@@ -602,7 +601,7 @@ cc2520_filter(struct ieee802154_dev *dev,
sizeof(panid), (u8 *)&panid); sizeof(panid), (u8 *)&panid);
} }
if (changed & IEEE802515_AFILT_IEEEADDR_CHANGED) { if (changed & IEEE802154_AFILT_IEEEADDR_CHANGED) {
dev_vdbg(&priv->spi->dev, dev_vdbg(&priv->spi->dev,
"cc2520_filter called for IEEE addr\n"); "cc2520_filter called for IEEE addr\n");
cc2520_write_ram(priv, CC2520RAM_IEEEADDR, cc2520_write_ram(priv, CC2520RAM_IEEEADDR,
...@@ -610,7 +609,7 @@ cc2520_filter(struct ieee802154_dev *dev, ...@@ -610,7 +609,7 @@ cc2520_filter(struct ieee802154_dev *dev,
(u8 *)&filt->ieee_addr); (u8 *)&filt->ieee_addr);
} }
if (changed & IEEE802515_AFILT_SADDR_CHANGED) { if (changed & IEEE802154_AFILT_SADDR_CHANGED) {
u16 addr = le16_to_cpu(filt->short_addr); u16 addr = le16_to_cpu(filt->short_addr);
dev_vdbg(&priv->spi->dev, dev_vdbg(&priv->spi->dev,
...@@ -619,7 +618,7 @@ cc2520_filter(struct ieee802154_dev *dev, ...@@ -619,7 +618,7 @@ cc2520_filter(struct ieee802154_dev *dev,
sizeof(addr), (u8 *)&addr); sizeof(addr), (u8 *)&addr);
} }
if (changed & IEEE802515_AFILT_PANC_CHANGED) { if (changed & IEEE802154_AFILT_PANC_CHANGED) {
dev_vdbg(&priv->spi->dev, dev_vdbg(&priv->spi->dev,
"cc2520_filter called for panc change\n"); "cc2520_filter called for panc change\n");
if (filt->pan_coord) if (filt->pan_coord)
...@@ -631,11 +630,11 @@ cc2520_filter(struct ieee802154_dev *dev, ...@@ -631,11 +630,11 @@ cc2520_filter(struct ieee802154_dev *dev,
return 0; return 0;
} }
static struct ieee802154_ops cc2520_ops = { static const struct ieee802154_ops cc2520_ops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.start = cc2520_start, .start = cc2520_start,
.stop = cc2520_stop, .stop = cc2520_stop,
.xmit = cc2520_tx, .xmit_sync = cc2520_tx,
.ed = cc2520_ed, .ed = cc2520_ed,
.set_channel = cc2520_set_channel, .set_channel = cc2520_set_channel,
.set_hw_addr_filt = cc2520_filter, .set_hw_addr_filt = cc2520_filter,
...@@ -645,27 +644,28 @@ static int cc2520_register(struct cc2520_private *priv) ...@@ -645,27 +644,28 @@ static int cc2520_register(struct cc2520_private *priv)
{ {
int ret = -ENOMEM; int ret = -ENOMEM;
priv->dev = ieee802154_alloc_device(sizeof(*priv), &cc2520_ops); priv->hw = ieee802154_alloc_hw(sizeof(*priv), &cc2520_ops);
if (!priv->dev) if (!priv->hw)
goto err_ret; goto err_ret;
priv->dev->priv = priv; priv->hw->priv = priv;
priv->dev->parent = &priv->spi->dev; priv->hw->parent = &priv->spi->dev;
priv->dev->extra_tx_headroom = 0; priv->hw->extra_tx_headroom = 0;
/* We do support only 2.4 Ghz */ /* We do support only 2.4 Ghz */
priv->dev->phy->channels_supported[0] = 0x7FFF800; priv->hw->phy->channels_supported[0] = 0x7FFF800;
priv->dev->flags = IEEE802154_HW_OMIT_CKSUM | IEEE802154_HW_AACK; priv->hw->flags = IEEE802154_HW_OMIT_CKSUM | IEEE802154_HW_AACK |
IEEE802154_HW_AFILT;
dev_vdbg(&priv->spi->dev, "registered cc2520\n"); dev_vdbg(&priv->spi->dev, "registered cc2520\n");
ret = ieee802154_register_device(priv->dev); ret = ieee802154_register_hw(priv->hw);
if (ret) if (ret)
goto err_free_device; goto err_free_device;
return 0; return 0;
err_free_device: err_free_device:
ieee802154_free_device(priv->dev); ieee802154_free_hw(priv->hw);
err_ret: err_ret:
return ret; return ret;
} }
...@@ -1002,8 +1002,8 @@ static int cc2520_remove(struct spi_device *spi) ...@@ -1002,8 +1002,8 @@ static int cc2520_remove(struct spi_device *spi)
mutex_destroy(&priv->buffer_mutex); mutex_destroy(&priv->buffer_mutex);
flush_work(&priv->fifop_irqwork); flush_work(&priv->fifop_irqwork);
ieee802154_unregister_device(priv->dev); ieee802154_unregister_hw(priv->hw);
ieee802154_free_device(priv->dev); ieee802154_free_hw(priv->hw);
return 0; return 0;
} }
......
/*
* Sample driver for HardMAC IEEE 802.15.4 devices
*
* Copyright (C) 2009 Siemens AG
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License 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.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Written by:
* Dmitry Eremin-Solenikov <dmitry.baryshkov@siemens.com>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/if_arp.h>
#include <net/af_ieee802154.h>
#include <net/ieee802154_netdev.h>
#include <net/ieee802154.h>
#include <net/nl802154.h>
#include <net/wpan-phy.h>
struct fakehard_priv {
struct wpan_phy *phy;
};
static struct wpan_phy *fake_to_phy(const struct net_device *dev)
{
struct fakehard_priv *priv = netdev_priv(dev);
return priv->phy;
}
/**
* fake_get_phy - Return a phy corresponding to this device.
* @dev: The network device for which to return the wan-phy object
*
* This function returns a wpan-phy object corresponding to the passed
* network device. Reference counter for wpan-phy object is incremented,
* so when the wpan-phy isn't necessary, you should drop the reference
* via @wpan_phy_put() call.
*/
static struct wpan_phy *fake_get_phy(const struct net_device *dev)
{
struct wpan_phy *phy = fake_to_phy(dev);
return to_phy(get_device(&phy->dev));
}
/**
* fake_get_pan_id - Retrieve the PAN ID of the device.
* @dev: The network device to retrieve the PAN of.
*
* Return the ID of the PAN from the PIB.
*/
static __le16 fake_get_pan_id(const struct net_device *dev)
{
BUG_ON(dev->type != ARPHRD_IEEE802154);
return cpu_to_le16(0xeba1);
}
/**
* fake_get_short_addr - Retrieve the short address of the device.
* @dev: The network device to retrieve the short address of.
*
* Returns the IEEE 802.15.4 short-form address cached for this
* device. If the device has not yet had a short address assigned
* then this should return 0xFFFF to indicate a lack of association.
*/
static __le16 fake_get_short_addr(const struct net_device *dev)
{
BUG_ON(dev->type != ARPHRD_IEEE802154);
return cpu_to_le16(0x1);
}
/**
* fake_get_dsn - Retrieve the DSN of the device.
* @dev: The network device to retrieve the DSN for.
*
* Returns the IEEE 802.15.4 DSN for the network device.
* The DSN is the sequence number which will be added to each
* packet or MAC command frame by the MAC during transmission.
*
* DSN means 'Data Sequence Number'.
*
* Note: This is in section 7.2.1.2 of the IEEE 802.15.4-2006
* document.
*/
static u8 fake_get_dsn(const struct net_device *dev)
{
BUG_ON(dev->type != ARPHRD_IEEE802154);
return 0x00; /* DSN are implemented in HW, so return just 0 */
}
/**
* fake_assoc_req - Make an association request to the HW.
* @dev: The network device which we are associating to a network.
* @addr: The coordinator with which we wish to associate.
* @channel: The channel on which to associate.
* @cap: The capability information field to use in the association.
*
* Start an association with a coordinator. The coordinator's address
* and PAN ID can be found in @addr.
*
* Note: This is in section 7.3.1 and 7.5.3.1 of the IEEE
* 802.15.4-2006 document.
*/
static int fake_assoc_req(struct net_device *dev,
struct ieee802154_addr *addr, u8 channel, u8 page, u8 cap)
{
struct wpan_phy *phy = fake_to_phy(dev);
mutex_lock(&phy->pib_lock);
phy->current_channel = channel;
phy->current_page = page;
mutex_unlock(&phy->pib_lock);
/* We simply emulate it here */
return ieee802154_nl_assoc_confirm(dev, fake_get_short_addr(dev),
IEEE802154_SUCCESS);
}
/**
* fake_assoc_resp - Send an association response to a device.
* @dev: The network device on which to send the response.
* @addr: The address of the device to respond to.
* @short_addr: The assigned short address for the device (if any).
* @status: The result of the association request.
*
* Queue the association response of the coordinator to another
* device's attempt to associate with the network which we
* coordinate. This is then added to the indirect-send queue to be
* transmitted to the end device when it polls for data.
*
* Note: This is in section 7.3.2 and 7.5.3.1 of the IEEE
* 802.15.4-2006 document.
*/
static int fake_assoc_resp(struct net_device *dev,
struct ieee802154_addr *addr, __le16 short_addr, u8 status)
{
return 0;
}
/**
* fake_disassoc_req - Disassociate a device from a network.
* @dev: The network device on which we're disassociating a device.
* @addr: The device to disassociate from the network.
* @reason: The reason to give to the device for being disassociated.
*
* This sends a disassociation notification to the device being
* disassociated from the network.
*
* Note: This is in section 7.5.3.2 of the IEEE 802.15.4-2006
* document, with the reason described in 7.3.3.2.
*/
static int fake_disassoc_req(struct net_device *dev,
struct ieee802154_addr *addr, u8 reason)
{
return ieee802154_nl_disassoc_confirm(dev, IEEE802154_SUCCESS);
}
/**
* fake_start_req - Start an IEEE 802.15.4 PAN.
* @dev: The network device on which to start the PAN.
* @addr: The coordinator address to use when starting the PAN.
* @channel: The channel on which to start the PAN.
* @bcn_ord: Beacon order.
* @sf_ord: Superframe order.
* @pan_coord: Whether or not we are the PAN coordinator or just
* requesting a realignment perhaps?
* @blx: Battery Life Extension feature bitfield.
* @coord_realign: Something to realign something else.
*
* If pan_coord is non-zero then this starts a network with the
* provided parameters, otherwise it attempts a coordinator
* realignment of the stated network instead.
*
* Note: This is in section 7.5.2.3 of the IEEE 802.15.4-2006
* document, with 7.3.8 describing coordinator realignment.
*/
static int fake_start_req(struct net_device *dev,
struct ieee802154_addr *addr, u8 channel, u8 page,
u8 bcn_ord, u8 sf_ord, u8 pan_coord, u8 blx,
u8 coord_realign)
{
struct wpan_phy *phy = fake_to_phy(dev);
mutex_lock(&phy->pib_lock);
phy->current_channel = channel;
phy->current_page = page;
mutex_unlock(&phy->pib_lock);
/* We don't emulate beacons here at all, so START should fail */
ieee802154_nl_start_confirm(dev, IEEE802154_INVALID_PARAMETER);
return 0;
}
/**
* fake_scan_req - Start a channel scan.
* @dev: The network device on which to perform a channel scan.
* @type: The type of scan to perform.
* @channels: The channel bitmask to scan.
* @duration: How long to spend on each channel.
*
* This starts either a passive (energy) scan or an active (PAN) scan
* on the channels indicated in the @channels bitmask. The duration of
* the scan is measured in terms of superframe duration. Specifically,
* the scan will spend aBaseSuperFrameDuration * ((2^n) + 1) on each
* channel.
*
* Note: This is in section 7.5.2.1 of the IEEE 802.15.4-2006 document.
*/
static int fake_scan_req(struct net_device *dev, u8 type, u32 channels,
u8 page, u8 duration)
{
u8 edl[27] = {};
return ieee802154_nl_scan_confirm(dev, IEEE802154_SUCCESS, type,
channels, page,
type == IEEE802154_MAC_SCAN_ED ? edl : NULL);
}
static struct ieee802154_mlme_ops fake_mlme = {
.assoc_req = fake_assoc_req,
.assoc_resp = fake_assoc_resp,
.disassoc_req = fake_disassoc_req,
.start_req = fake_start_req,
.scan_req = fake_scan_req,
.get_phy = fake_get_phy,
.get_pan_id = fake_get_pan_id,
.get_short_addr = fake_get_short_addr,
.get_dsn = fake_get_dsn,
};
static int ieee802154_fake_open(struct net_device *dev)
{
netif_start_queue(dev);
return 0;
}
static int ieee802154_fake_close(struct net_device *dev)
{
netif_stop_queue(dev);
return 0;
}
static netdev_tx_t ieee802154_fake_xmit(struct sk_buff *skb,
struct net_device *dev)
{
dev->stats.tx_packets++;
dev->stats.tx_bytes += skb->len;
/* FIXME: do hardware work here ... */
dev_kfree_skb(skb);
return NETDEV_TX_OK;
}
static int ieee802154_fake_ioctl(struct net_device *dev, struct ifreq *ifr,
int cmd)
{
struct sockaddr_ieee802154 *sa =
(struct sockaddr_ieee802154 *)&ifr->ifr_addr;
u16 pan_id, short_addr;
switch (cmd) {
case SIOCGIFADDR:
/* FIXME: fixed here, get from device IRL */
pan_id = le16_to_cpu(fake_get_pan_id(dev));
short_addr = le16_to_cpu(fake_get_short_addr(dev));
if (pan_id == IEEE802154_PANID_BROADCAST ||
short_addr == IEEE802154_ADDR_BROADCAST)
return -EADDRNOTAVAIL;
sa->family = AF_IEEE802154;
sa->addr.addr_type = IEEE802154_ADDR_SHORT;
sa->addr.pan_id = pan_id;
sa->addr.short_addr = short_addr;
return 0;
}
return -ENOIOCTLCMD;
}
static int ieee802154_fake_mac_addr(struct net_device *dev, void *p)
{
return -EBUSY; /* HW address is built into the device */
}
static const struct net_device_ops fake_ops = {
.ndo_open = ieee802154_fake_open,
.ndo_stop = ieee802154_fake_close,
.ndo_start_xmit = ieee802154_fake_xmit,
.ndo_do_ioctl = ieee802154_fake_ioctl,
.ndo_set_mac_address = ieee802154_fake_mac_addr,
};
static void ieee802154_fake_destruct(struct net_device *dev)
{
struct wpan_phy *phy = fake_to_phy(dev);
wpan_phy_unregister(phy);
free_netdev(dev);
wpan_phy_free(phy);
}
static void ieee802154_fake_setup(struct net_device *dev)
{
dev->addr_len = IEEE802154_ADDR_LEN;
memset(dev->broadcast, 0xff, IEEE802154_ADDR_LEN);
dev->features = NETIF_F_HW_CSUM;
dev->needed_tailroom = 2; /* FCS */
dev->mtu = 127;
dev->tx_queue_len = 10;
dev->type = ARPHRD_IEEE802154;
dev->flags = IFF_NOARP | IFF_BROADCAST;
dev->watchdog_timeo = 0;
dev->destructor = ieee802154_fake_destruct;
}
static int ieee802154fake_probe(struct platform_device *pdev)
{
struct net_device *dev;
struct fakehard_priv *priv;
struct wpan_phy *phy = wpan_phy_alloc(0);
int err;
if (!phy)
return -ENOMEM;
dev = alloc_netdev(sizeof(struct fakehard_priv), "hardwpan%d",
NET_NAME_UNKNOWN, ieee802154_fake_setup);
if (!dev) {
wpan_phy_free(phy);
return -ENOMEM;
}
memcpy(dev->dev_addr, "\xba\xbe\xca\xfe\xde\xad\xbe\xef",
dev->addr_len);
/*
* For now we'd like to emulate 2.4 GHz-only device,
* both O-QPSK and CSS
*/
/* 2.4 GHz O-QPSK 802.15.4-2003 */
phy->channels_supported[0] |= 0x7FFF800;
/* 2.4 GHz CSS 802.15.4a-2007 */
phy->channels_supported[3] |= 0x3fff;
phy->transmit_power = 0xbf;
dev->netdev_ops = &fake_ops;
dev->ml_priv = &fake_mlme;
priv = netdev_priv(dev);
priv->phy = phy;
wpan_phy_set_dev(phy, &pdev->dev);
SET_NETDEV_DEV(dev, &phy->dev);
platform_set_drvdata(pdev, dev);
err = wpan_phy_register(phy);
if (err)
goto out;
err = register_netdev(dev);
if (err < 0)
goto out;
dev_info(&pdev->dev, "Added ieee802154 HardMAC hardware\n");
return 0;
out:
unregister_netdev(dev);
return err;
}
static int ieee802154fake_remove(struct platform_device *pdev)
{
struct net_device *dev = platform_get_drvdata(pdev);
unregister_netdev(dev);
return 0;
}
static struct platform_device *ieee802154fake_dev;
static struct platform_driver ieee802154fake_driver = {
.probe = ieee802154fake_probe,
.remove = ieee802154fake_remove,
.driver = {
.name = "ieee802154hardmac",
.owner = THIS_MODULE,
},
};
static __init int fake_init(void)
{
ieee802154fake_dev = platform_device_register_simple(
"ieee802154hardmac", -1, NULL, 0);
return platform_driver_register(&ieee802154fake_driver);
}
static __exit void fake_exit(void)
{
platform_driver_unregister(&ieee802154fake_driver);
platform_device_unregister(ieee802154fake_dev);
}
module_init(fake_init);
module_exit(fake_exit);
MODULE_LICENSE("GPL");
...@@ -12,10 +12,6 @@ ...@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Written by: * Written by:
* Sergey Lapin <slapin@ossfans.org> * Sergey Lapin <slapin@ossfans.org>
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
...@@ -29,12 +25,12 @@ ...@@ -29,12 +25,12 @@
#include <linux/device.h> #include <linux/device.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <net/mac802154.h> #include <net/mac802154.h>
#include <net/wpan-phy.h> #include <net/cfg802154.h>
static int numlbs = 1; static int numlbs = 1;
struct fakelb_dev_priv { struct fakelb_dev_priv {
struct ieee802154_dev *dev; struct ieee802154_hw *hw;
struct list_head list; struct list_head list;
struct fakelb_priv *fake; struct fakelb_priv *fake;
...@@ -49,9 +45,8 @@ struct fakelb_priv { ...@@ -49,9 +45,8 @@ struct fakelb_priv {
}; };
static int static int
fakelb_hw_ed(struct ieee802154_dev *dev, u8 *level) fakelb_hw_ed(struct ieee802154_hw *hw, u8 *level)
{ {
might_sleep();
BUG_ON(!level); BUG_ON(!level);
*level = 0xbe; *level = 0xbe;
...@@ -59,13 +54,12 @@ fakelb_hw_ed(struct ieee802154_dev *dev, u8 *level) ...@@ -59,13 +54,12 @@ fakelb_hw_ed(struct ieee802154_dev *dev, u8 *level)
} }
static int static int
fakelb_hw_channel(struct ieee802154_dev *dev, int page, int channel) fakelb_hw_channel(struct ieee802154_hw *hw, u8 page, u8 channel)
{ {
pr_debug("set channel to %d\n", channel); pr_debug("set channel to %d\n", channel);
might_sleep(); hw->phy->current_page = page;
dev->phy->current_page = page; hw->phy->current_channel = channel;
dev->phy->current_channel = channel;
return 0; return 0;
} }
...@@ -78,19 +72,17 @@ fakelb_hw_deliver(struct fakelb_dev_priv *priv, struct sk_buff *skb) ...@@ -78,19 +72,17 @@ fakelb_hw_deliver(struct fakelb_dev_priv *priv, struct sk_buff *skb)
spin_lock(&priv->lock); spin_lock(&priv->lock);
if (priv->working) { if (priv->working) {
newskb = pskb_copy(skb, GFP_ATOMIC); newskb = pskb_copy(skb, GFP_ATOMIC);
ieee802154_rx_irqsafe(priv->dev, newskb, 0xcc); ieee802154_rx_irqsafe(priv->hw, newskb, 0xcc);
} }
spin_unlock(&priv->lock); spin_unlock(&priv->lock);
} }
static int static int
fakelb_hw_xmit(struct ieee802154_dev *dev, struct sk_buff *skb) fakelb_hw_xmit(struct ieee802154_hw *hw, struct sk_buff *skb)
{ {
struct fakelb_dev_priv *priv = dev->priv; struct fakelb_dev_priv *priv = hw->priv;
struct fakelb_priv *fake = priv->fake; struct fakelb_priv *fake = priv->fake;
might_sleep();
read_lock_bh(&fake->lock); read_lock_bh(&fake->lock);
if (priv->list.next == priv->list.prev) { if (priv->list.next == priv->list.prev) {
/* we are the only one device */ /* we are the only one device */
...@@ -99,8 +91,8 @@ fakelb_hw_xmit(struct ieee802154_dev *dev, struct sk_buff *skb) ...@@ -99,8 +91,8 @@ fakelb_hw_xmit(struct ieee802154_dev *dev, struct sk_buff *skb)
struct fakelb_dev_priv *dp; struct fakelb_dev_priv *dp;
list_for_each_entry(dp, &priv->fake->list, list) { list_for_each_entry(dp, &priv->fake->list, list) {
if (dp != priv && if (dp != priv &&
(dp->dev->phy->current_channel == (dp->hw->phy->current_channel ==
priv->dev->phy->current_channel)) priv->hw->phy->current_channel))
fakelb_hw_deliver(dp, skb); fakelb_hw_deliver(dp, skb);
} }
} }
...@@ -110,8 +102,8 @@ fakelb_hw_xmit(struct ieee802154_dev *dev, struct sk_buff *skb) ...@@ -110,8 +102,8 @@ fakelb_hw_xmit(struct ieee802154_dev *dev, struct sk_buff *skb)
} }
static int static int
fakelb_hw_start(struct ieee802154_dev *dev) { fakelb_hw_start(struct ieee802154_hw *hw) {
struct fakelb_dev_priv *priv = dev->priv; struct fakelb_dev_priv *priv = hw->priv;
int ret = 0; int ret = 0;
spin_lock(&priv->lock); spin_lock(&priv->lock);
...@@ -125,17 +117,17 @@ fakelb_hw_start(struct ieee802154_dev *dev) { ...@@ -125,17 +117,17 @@ fakelb_hw_start(struct ieee802154_dev *dev) {
} }
static void static void
fakelb_hw_stop(struct ieee802154_dev *dev) { fakelb_hw_stop(struct ieee802154_hw *hw) {
struct fakelb_dev_priv *priv = dev->priv; struct fakelb_dev_priv *priv = hw->priv;
spin_lock(&priv->lock); spin_lock(&priv->lock);
priv->working = 0; priv->working = 0;
spin_unlock(&priv->lock); spin_unlock(&priv->lock);
} }
static struct ieee802154_ops fakelb_ops = { static const struct ieee802154_ops fakelb_ops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.xmit = fakelb_hw_xmit, .xmit_sync = fakelb_hw_xmit,
.ed = fakelb_hw_ed, .ed = fakelb_hw_ed,
.set_channel = fakelb_hw_channel, .set_channel = fakelb_hw_channel,
.start = fakelb_hw_start, .start = fakelb_hw_start,
...@@ -150,54 +142,54 @@ static int fakelb_add_one(struct device *dev, struct fakelb_priv *fake) ...@@ -150,54 +142,54 @@ static int fakelb_add_one(struct device *dev, struct fakelb_priv *fake)
{ {
struct fakelb_dev_priv *priv; struct fakelb_dev_priv *priv;
int err; int err;
struct ieee802154_dev *ieee; struct ieee802154_hw *hw;
ieee = ieee802154_alloc_device(sizeof(*priv), &fakelb_ops); hw = ieee802154_alloc_hw(sizeof(*priv), &fakelb_ops);
if (!ieee) if (!hw)
return -ENOMEM; return -ENOMEM;
priv = ieee->priv; priv = hw->priv;
priv->dev = ieee; priv->hw = hw;
/* 868 MHz BPSK 802.15.4-2003 */ /* 868 MHz BPSK 802.15.4-2003 */
ieee->phy->channels_supported[0] |= 1; hw->phy->channels_supported[0] |= 1;
/* 915 MHz BPSK 802.15.4-2003 */ /* 915 MHz BPSK 802.15.4-2003 */
ieee->phy->channels_supported[0] |= 0x7fe; hw->phy->channels_supported[0] |= 0x7fe;
/* 2.4 GHz O-QPSK 802.15.4-2003 */ /* 2.4 GHz O-QPSK 802.15.4-2003 */
ieee->phy->channels_supported[0] |= 0x7FFF800; hw->phy->channels_supported[0] |= 0x7FFF800;
/* 868 MHz ASK 802.15.4-2006 */ /* 868 MHz ASK 802.15.4-2006 */
ieee->phy->channels_supported[1] |= 1; hw->phy->channels_supported[1] |= 1;
/* 915 MHz ASK 802.15.4-2006 */ /* 915 MHz ASK 802.15.4-2006 */
ieee->phy->channels_supported[1] |= 0x7fe; hw->phy->channels_supported[1] |= 0x7fe;
/* 868 MHz O-QPSK 802.15.4-2006 */ /* 868 MHz O-QPSK 802.15.4-2006 */
ieee->phy->channels_supported[2] |= 1; hw->phy->channels_supported[2] |= 1;
/* 915 MHz O-QPSK 802.15.4-2006 */ /* 915 MHz O-QPSK 802.15.4-2006 */
ieee->phy->channels_supported[2] |= 0x7fe; hw->phy->channels_supported[2] |= 0x7fe;
/* 2.4 GHz CSS 802.15.4a-2007 */ /* 2.4 GHz CSS 802.15.4a-2007 */
ieee->phy->channels_supported[3] |= 0x3fff; hw->phy->channels_supported[3] |= 0x3fff;
/* UWB Sub-gigahertz 802.15.4a-2007 */ /* UWB Sub-gigahertz 802.15.4a-2007 */
ieee->phy->channels_supported[4] |= 1; hw->phy->channels_supported[4] |= 1;
/* UWB Low band 802.15.4a-2007 */ /* UWB Low band 802.15.4a-2007 */
ieee->phy->channels_supported[4] |= 0x1e; hw->phy->channels_supported[4] |= 0x1e;
/* UWB High band 802.15.4a-2007 */ /* UWB High band 802.15.4a-2007 */
ieee->phy->channels_supported[4] |= 0xffe0; hw->phy->channels_supported[4] |= 0xffe0;
/* 750 MHz O-QPSK 802.15.4c-2009 */ /* 750 MHz O-QPSK 802.15.4c-2009 */
ieee->phy->channels_supported[5] |= 0xf; hw->phy->channels_supported[5] |= 0xf;
/* 750 MHz MPSK 802.15.4c-2009 */ /* 750 MHz MPSK 802.15.4c-2009 */
ieee->phy->channels_supported[5] |= 0xf0; hw->phy->channels_supported[5] |= 0xf0;
/* 950 MHz BPSK 802.15.4d-2009 */ /* 950 MHz BPSK 802.15.4d-2009 */
ieee->phy->channels_supported[6] |= 0x3ff; hw->phy->channels_supported[6] |= 0x3ff;
/* 950 MHz GFSK 802.15.4d-2009 */ /* 950 MHz GFSK 802.15.4d-2009 */
ieee->phy->channels_supported[6] |= 0x3ffc00; hw->phy->channels_supported[6] |= 0x3ffc00;
INIT_LIST_HEAD(&priv->list); INIT_LIST_HEAD(&priv->list);
priv->fake = fake; priv->fake = fake;
spin_lock_init(&priv->lock); spin_lock_init(&priv->lock);
ieee->parent = dev; hw->parent = dev;
err = ieee802154_register_device(ieee); err = ieee802154_register_hw(hw);
if (err) if (err)
goto err_reg; goto err_reg;
...@@ -208,7 +200,7 @@ static int fakelb_add_one(struct device *dev, struct fakelb_priv *fake) ...@@ -208,7 +200,7 @@ static int fakelb_add_one(struct device *dev, struct fakelb_priv *fake)
return 0; return 0;
err_reg: err_reg:
ieee802154_free_device(priv->dev); ieee802154_free_hw(priv->hw);
return err; return err;
} }
...@@ -218,8 +210,8 @@ static void fakelb_del(struct fakelb_dev_priv *priv) ...@@ -218,8 +210,8 @@ static void fakelb_del(struct fakelb_dev_priv *priv)
list_del(&priv->list); list_del(&priv->list);
write_unlock_bh(&priv->fake->lock); write_unlock_bh(&priv->fake->lock);
ieee802154_unregister_device(priv->dev); ieee802154_unregister_hw(priv->hw);
ieee802154_free_device(priv->dev); ieee802154_free_hw(priv->hw);
} }
static int fakelb_probe(struct platform_device *pdev) static int fakelb_probe(struct platform_device *pdev)
......
...@@ -13,18 +13,14 @@ ...@@ -13,18 +13,14 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/ */
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/module.h> #include <linux/module.h>
#include <net/wpan-phy.h> #include <linux/ieee802154.h>
#include <net/cfg802154.h>
#include <net/mac802154.h> #include <net/mac802154.h>
#include <net/ieee802154.h>
/* MRF24J40 Short Address Registers */ /* MRF24J40 Short Address Registers */
#define REG_RXMCR 0x00 /* Receive MAC control */ #define REG_RXMCR 0x00 /* Receive MAC control */
...@@ -43,6 +39,8 @@ ...@@ -43,6 +39,8 @@
#define REG_TXSTBL 0x2E /* TX Stabilization */ #define REG_TXSTBL 0x2E /* TX Stabilization */
#define REG_INTSTAT 0x31 /* Interrupt Status */ #define REG_INTSTAT 0x31 /* Interrupt Status */
#define REG_INTCON 0x32 /* Interrupt Control */ #define REG_INTCON 0x32 /* Interrupt Control */
#define REG_GPIO 0x33 /* GPIO */
#define REG_TRISGPIO 0x34 /* GPIO direction */
#define REG_RFCTL 0x36 /* RF Control Mode Register */ #define REG_RFCTL 0x36 /* RF Control Mode Register */
#define REG_BBREG1 0x39 /* Baseband Registers */ #define REG_BBREG1 0x39 /* Baseband Registers */
#define REG_BBREG2 0x3A /* */ #define REG_BBREG2 0x3A /* */
...@@ -63,6 +61,7 @@ ...@@ -63,6 +61,7 @@
#define REG_SLPCON1 0x220 #define REG_SLPCON1 0x220
#define REG_WAKETIMEL 0x222 /* Wake-up Time Match Value Low */ #define REG_WAKETIMEL 0x222 /* Wake-up Time Match Value Low */
#define REG_WAKETIMEH 0x223 /* Wake-up Time Match Value High */ #define REG_WAKETIMEH 0x223 /* Wake-up Time Match Value High */
#define REG_TESTMODE 0x22F /* Test mode */
#define REG_RX_FIFO 0x300 /* Receive FIFO */ #define REG_RX_FIFO 0x300 /* Receive FIFO */
/* Device configuration: Only channels 11-26 on page 0 are supported. */ /* Device configuration: Only channels 11-26 on page 0 are supported. */
...@@ -75,10 +74,12 @@ ...@@ -75,10 +74,12 @@
#define RX_FIFO_SIZE 144 /* From datasheet */ #define RX_FIFO_SIZE 144 /* From datasheet */
#define SET_CHANNEL_DELAY_US 192 /* From datasheet */ #define SET_CHANNEL_DELAY_US 192 /* From datasheet */
enum mrf24j40_modules { MRF24J40, MRF24J40MA, MRF24J40MC };
/* Device Private Data */ /* Device Private Data */
struct mrf24j40 { struct mrf24j40 {
struct spi_device *spi; struct spi_device *spi;
struct ieee802154_dev *dev; struct ieee802154_hw *hw;
struct mutex buffer_mutex; /* only used to protect buf */ struct mutex buffer_mutex; /* only used to protect buf */
struct completion tx_complete; struct completion tx_complete;
...@@ -331,9 +332,9 @@ static int mrf24j40_read_rx_buf(struct mrf24j40 *devrec, ...@@ -331,9 +332,9 @@ static int mrf24j40_read_rx_buf(struct mrf24j40 *devrec,
return ret; return ret;
} }
static int mrf24j40_tx(struct ieee802154_dev *dev, struct sk_buff *skb) static int mrf24j40_tx(struct ieee802154_hw *hw, struct sk_buff *skb)
{ {
struct mrf24j40 *devrec = dev->priv; struct mrf24j40 *devrec = hw->priv;
u8 val; u8 val;
int ret = 0; int ret = 0;
...@@ -382,7 +383,7 @@ static int mrf24j40_tx(struct ieee802154_dev *dev, struct sk_buff *skb) ...@@ -382,7 +383,7 @@ static int mrf24j40_tx(struct ieee802154_dev *dev, struct sk_buff *skb)
return ret; return ret;
} }
static int mrf24j40_ed(struct ieee802154_dev *dev, u8 *level) static int mrf24j40_ed(struct ieee802154_hw *hw, u8 *level)
{ {
/* TODO: */ /* TODO: */
pr_warn("mrf24j40: ed not implemented\n"); pr_warn("mrf24j40: ed not implemented\n");
...@@ -390,9 +391,9 @@ static int mrf24j40_ed(struct ieee802154_dev *dev, u8 *level) ...@@ -390,9 +391,9 @@ static int mrf24j40_ed(struct ieee802154_dev *dev, u8 *level)
return 0; return 0;
} }
static int mrf24j40_start(struct ieee802154_dev *dev) static int mrf24j40_start(struct ieee802154_hw *hw)
{ {
struct mrf24j40 *devrec = dev->priv; struct mrf24j40 *devrec = hw->priv;
u8 val; u8 val;
int ret; int ret;
...@@ -407,9 +408,9 @@ static int mrf24j40_start(struct ieee802154_dev *dev) ...@@ -407,9 +408,9 @@ static int mrf24j40_start(struct ieee802154_dev *dev)
return 0; return 0;
} }
static void mrf24j40_stop(struct ieee802154_dev *dev) static void mrf24j40_stop(struct ieee802154_hw *hw)
{ {
struct mrf24j40 *devrec = dev->priv; struct mrf24j40 *devrec = hw->priv;
u8 val; u8 val;
int ret; int ret;
...@@ -422,10 +423,9 @@ static void mrf24j40_stop(struct ieee802154_dev *dev) ...@@ -422,10 +423,9 @@ static void mrf24j40_stop(struct ieee802154_dev *dev)
write_short_reg(devrec, REG_INTCON, val); write_short_reg(devrec, REG_INTCON, val);
} }
static int mrf24j40_set_channel(struct ieee802154_dev *dev, static int mrf24j40_set_channel(struct ieee802154_hw *hw, u8 page, u8 channel)
int page, int channel)
{ {
struct mrf24j40 *devrec = dev->priv; struct mrf24j40 *devrec = hw->priv;
u8 val; u8 val;
int ret; int ret;
...@@ -453,15 +453,15 @@ static int mrf24j40_set_channel(struct ieee802154_dev *dev, ...@@ -453,15 +453,15 @@ static int mrf24j40_set_channel(struct ieee802154_dev *dev,
return 0; return 0;
} }
static int mrf24j40_filter(struct ieee802154_dev *dev, static int mrf24j40_filter(struct ieee802154_hw *hw,
struct ieee802154_hw_addr_filt *filt, struct ieee802154_hw_addr_filt *filt,
unsigned long changed) unsigned long changed)
{ {
struct mrf24j40 *devrec = dev->priv; struct mrf24j40 *devrec = hw->priv;
dev_dbg(printdev(devrec), "filter\n"); dev_dbg(printdev(devrec), "filter\n");
if (changed & IEEE802515_AFILT_SADDR_CHANGED) { if (changed & IEEE802154_AFILT_SADDR_CHANGED) {
/* Short Addr */ /* Short Addr */
u8 addrh, addrl; u8 addrh, addrl;
...@@ -474,7 +474,7 @@ static int mrf24j40_filter(struct ieee802154_dev *dev, ...@@ -474,7 +474,7 @@ static int mrf24j40_filter(struct ieee802154_dev *dev,
"Set short addr to %04hx\n", filt->short_addr); "Set short addr to %04hx\n", filt->short_addr);
} }
if (changed & IEEE802515_AFILT_IEEEADDR_CHANGED) { if (changed & IEEE802154_AFILT_IEEEADDR_CHANGED) {
/* Device Address */ /* Device Address */
u8 i, addr[8]; u8 i, addr[8];
...@@ -490,7 +490,7 @@ static int mrf24j40_filter(struct ieee802154_dev *dev, ...@@ -490,7 +490,7 @@ static int mrf24j40_filter(struct ieee802154_dev *dev,
#endif #endif
} }
if (changed & IEEE802515_AFILT_PANID_CHANGED) { if (changed & IEEE802154_AFILT_PANID_CHANGED) {
/* PAN ID */ /* PAN ID */
u8 panidl, panidh; u8 panidl, panidh;
...@@ -502,7 +502,7 @@ static int mrf24j40_filter(struct ieee802154_dev *dev, ...@@ -502,7 +502,7 @@ static int mrf24j40_filter(struct ieee802154_dev *dev,
dev_dbg(printdev(devrec), "Set PANID to %04hx\n", filt->pan_id); dev_dbg(printdev(devrec), "Set PANID to %04hx\n", filt->pan_id);
} }
if (changed & IEEE802515_AFILT_PANC_CHANGED) { if (changed & IEEE802154_AFILT_PANC_CHANGED) {
/* Pan Coordinator */ /* Pan Coordinator */
u8 val; u8 val;
int ret; int ret;
...@@ -543,7 +543,7 @@ static int mrf24j40_handle_rx(struct mrf24j40 *devrec) ...@@ -543,7 +543,7 @@ static int mrf24j40_handle_rx(struct mrf24j40 *devrec)
val |= 4; /* SET RXDECINV */ val |= 4; /* SET RXDECINV */
write_short_reg(devrec, REG_BBREG1, val); write_short_reg(devrec, REG_BBREG1, val);
skb = alloc_skb(len, GFP_KERNEL); skb = dev_alloc_skb(len);
if (!skb) { if (!skb) {
ret = -ENOMEM; ret = -ENOMEM;
goto out; goto out;
...@@ -563,7 +563,7 @@ static int mrf24j40_handle_rx(struct mrf24j40 *devrec) ...@@ -563,7 +563,7 @@ static int mrf24j40_handle_rx(struct mrf24j40 *devrec)
/* TODO: Other drivers call ieee20154_rx_irqsafe() here (eg: cc2040, /* TODO: Other drivers call ieee20154_rx_irqsafe() here (eg: cc2040,
* also from a workqueue). I think irqsafe is not necessary here. * also from a workqueue). I think irqsafe is not necessary here.
* Can someone confirm? */ * Can someone confirm? */
ieee802154_rx_irqsafe(devrec->dev, skb, lqi); ieee802154_rx_irqsafe(devrec->hw, skb, lqi);
dev_dbg(printdev(devrec), "RX Handled\n"); dev_dbg(printdev(devrec), "RX Handled\n");
...@@ -578,9 +578,9 @@ static int mrf24j40_handle_rx(struct mrf24j40 *devrec) ...@@ -578,9 +578,9 @@ static int mrf24j40_handle_rx(struct mrf24j40 *devrec)
return ret; return ret;
} }
static struct ieee802154_ops mrf24j40_ops = { static const struct ieee802154_ops mrf24j40_ops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.xmit = mrf24j40_tx, .xmit_sync = mrf24j40_tx,
.ed = mrf24j40_ed, .ed = mrf24j40_ed,
.start = mrf24j40_start, .start = mrf24j40_start,
.stop = mrf24j40_stop, .stop = mrf24j40_stop,
...@@ -691,6 +691,28 @@ static int mrf24j40_hw_init(struct mrf24j40 *devrec) ...@@ -691,6 +691,28 @@ static int mrf24j40_hw_init(struct mrf24j40 *devrec)
if (ret) if (ret)
goto err_ret; goto err_ret;
if (spi_get_device_id(devrec->spi)->driver_data == MRF24J40MC) {
/* Enable external amplifier.
* From MRF24J40MC datasheet section 1.3: Operation.
*/
read_long_reg(devrec, REG_TESTMODE, &val);
val |= 0x7; /* Configure GPIO 0-2 to control amplifier */
write_long_reg(devrec, REG_TESTMODE, val);
read_short_reg(devrec, REG_TRISGPIO, &val);
val |= 0x8; /* Set GPIO3 as output. */
write_short_reg(devrec, REG_TRISGPIO, val);
read_short_reg(devrec, REG_GPIO, &val);
val |= 0x8; /* Set GPIO3 HIGH to enable U5 voltage regulator */
write_short_reg(devrec, REG_GPIO, val);
/* Reduce TX pwr to meet FCC requirements.
* From MRF24J40MC datasheet section 3.1.1
*/
write_long_reg(devrec, REG_RFCON3, 0x28);
}
return 0; return 0;
err_ret: err_ret:
...@@ -722,17 +744,18 @@ static int mrf24j40_probe(struct spi_device *spi) ...@@ -722,17 +744,18 @@ static int mrf24j40_probe(struct spi_device *spi)
/* Register with the 802154 subsystem */ /* Register with the 802154 subsystem */
devrec->dev = ieee802154_alloc_device(0, &mrf24j40_ops); devrec->hw = ieee802154_alloc_hw(0, &mrf24j40_ops);
if (!devrec->dev) if (!devrec->hw)
goto err_ret; goto err_ret;
devrec->dev->priv = devrec; devrec->hw->priv = devrec;
devrec->dev->parent = &devrec->spi->dev; devrec->hw->parent = &devrec->spi->dev;
devrec->dev->phy->channels_supported[0] = CHANNEL_MASK; devrec->hw->phy->channels_supported[0] = CHANNEL_MASK;
devrec->dev->flags = IEEE802154_HW_OMIT_CKSUM|IEEE802154_HW_AACK; devrec->hw->flags = IEEE802154_HW_OMIT_CKSUM | IEEE802154_HW_AACK |
IEEE802154_HW_AFILT;
dev_dbg(printdev(devrec), "registered mrf24j40\n"); dev_dbg(printdev(devrec), "registered mrf24j40\n");
ret = ieee802154_register_device(devrec->dev); ret = ieee802154_register_hw(devrec->hw);
if (ret) if (ret)
goto err_register_device; goto err_register_device;
...@@ -757,9 +780,9 @@ static int mrf24j40_probe(struct spi_device *spi) ...@@ -757,9 +780,9 @@ static int mrf24j40_probe(struct spi_device *spi)
err_irq: err_irq:
err_hw_init: err_hw_init:
ieee802154_unregister_device(devrec->dev); ieee802154_unregister_hw(devrec->hw);
err_register_device: err_register_device:
ieee802154_free_device(devrec->dev); ieee802154_free_hw(devrec->hw);
err_ret: err_ret:
return ret; return ret;
} }
...@@ -770,8 +793,8 @@ static int mrf24j40_remove(struct spi_device *spi) ...@@ -770,8 +793,8 @@ static int mrf24j40_remove(struct spi_device *spi)
dev_dbg(printdev(devrec), "remove\n"); dev_dbg(printdev(devrec), "remove\n");
ieee802154_unregister_device(devrec->dev); ieee802154_unregister_hw(devrec->hw);
ieee802154_free_device(devrec->dev); ieee802154_free_hw(devrec->hw);
/* TODO: Will ieee802154_free_device() wait until ->xmit() is /* TODO: Will ieee802154_free_device() wait until ->xmit() is
* complete? */ * complete? */
...@@ -779,8 +802,9 @@ static int mrf24j40_remove(struct spi_device *spi) ...@@ -779,8 +802,9 @@ static int mrf24j40_remove(struct spi_device *spi)
} }
static const struct spi_device_id mrf24j40_ids[] = { static const struct spi_device_id mrf24j40_ids[] = {
{ "mrf24j40", 0 }, { "mrf24j40", MRF24J40 },
{ "mrf24j40ma", 0 }, { "mrf24j40ma", MRF24J40MA },
{ "mrf24j40mc", MRF24J40MC },
{ }, { },
}; };
MODULE_DEVICE_TABLE(spi, mrf24j40_ids); MODULE_DEVICE_TABLE(spi, mrf24j40_ids);
......
...@@ -12,10 +12,6 @@ ...@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Written by: * Written by:
* Pavel Smolenskiy <pavel.smolenskiy@gmail.com> * Pavel Smolenskiy <pavel.smolenskiy@gmail.com>
* Maxim Gorbachyov <maxim.gorbachev@siemens.com> * Maxim Gorbachyov <maxim.gorbachev@siemens.com>
...@@ -24,10 +20,13 @@ ...@@ -24,10 +20,13 @@
* Alexander Smirnov <alex.bluesman.smirnov@gmail.com> * Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
*/ */
#ifndef NET_IEEE802154_H #ifndef LINUX_IEEE802154_H
#define NET_IEEE802154_H #define LINUX_IEEE802154_H
#include <linux/types.h>
#define IEEE802154_MTU 127 #define IEEE802154_MTU 127
#define IEEE802154_MIN_PSDU_LEN 5
#define IEEE802154_FC_TYPE_BEACON 0x0 /* Frame is beacon */ #define IEEE802154_FC_TYPE_BEACON 0x0 /* Frame is beacon */
#define IEEE802154_FC_TYPE_DATA 0x1 /* Frame is data */ #define IEEE802154_FC_TYPE_DATA 0x1 /* Frame is data */
...@@ -189,7 +188,13 @@ enum { ...@@ -189,7 +188,13 @@ enum {
IEEE802154_SCAN_IN_PROGRESS = 0xfc, IEEE802154_SCAN_IN_PROGRESS = 0xfc,
}; };
/**
* ieee802154_is_valid_psdu_len - check if psdu len is valid
* @len: psdu len with (MHR + payload + MFR)
*/
static inline bool ieee802154_is_valid_psdu_len(const u8 len)
{
return (len >= IEEE802154_MIN_PSDU_LEN && len <= IEEE802154_MTU);
}
#endif #endif /* LINUX_IEEE802154_H */
...@@ -12,10 +12,6 @@ ...@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/ */
#ifndef NL802154_H #ifndef NL802154_H
......
...@@ -372,12 +372,12 @@ lowpan_uncompress_size(const struct sk_buff *skb, u16 *dgram_offset) ...@@ -372,12 +372,12 @@ lowpan_uncompress_size(const struct sk_buff *skb, u16 *dgram_offset)
return skb->len + uncomp_header - ret; return skb->len + uncomp_header - ret;
} }
typedef int (*skb_delivery_cb)(struct sk_buff *skb, struct net_device *dev); int
lowpan_header_decompress(struct sk_buff *skb, struct net_device *dev,
int lowpan_process_data(struct sk_buff *skb, struct net_device *dev, const u8 *saddr, const u8 saddr_type,
const u8 *saddr, const u8 saddr_type, const u8 saddr_len, const u8 saddr_len, const u8 *daddr,
const u8 *daddr, const u8 daddr_type, const u8 daddr_len, const u8 daddr_type, const u8 daddr_len,
u8 iphc0, u8 iphc1, skb_delivery_cb skb_deliver); u8 iphc0, u8 iphc1);
int lowpan_header_compress(struct sk_buff *skb, struct net_device *dev, int lowpan_header_compress(struct sk_buff *skb, struct net_device *dev,
unsigned short type, const void *_daddr, unsigned short type, const void *_daddr,
const void *_saddr, unsigned int len); const void *_saddr, unsigned int len);
......
...@@ -12,10 +12,6 @@ ...@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Written by: * Written by:
* Sergey Lapin <slapin@ossfans.org> * Sergey Lapin <slapin@ossfans.org>
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
......
...@@ -398,6 +398,8 @@ struct hci_conn { ...@@ -398,6 +398,8 @@ struct hci_conn {
__u16 le_conn_interval; __u16 le_conn_interval;
__u16 le_conn_latency; __u16 le_conn_latency;
__u16 le_supv_timeout; __u16 le_supv_timeout;
__u8 le_adv_data[HCI_MAX_AD_LENGTH];
__u8 le_adv_data_len;
__s8 rssi; __s8 rssi;
__s8 tx_power; __s8 tx_power;
__s8 max_tx_power; __s8 max_tx_power;
...@@ -553,6 +555,7 @@ enum { ...@@ -553,6 +555,7 @@ enum {
HCI_CONN_STK_ENCRYPT, HCI_CONN_STK_ENCRYPT,
HCI_CONN_AUTH_INITIATOR, HCI_CONN_AUTH_INITIATOR,
HCI_CONN_DROP, HCI_CONN_DROP,
HCI_CONN_PARAM_REMOVAL_PEND,
}; };
static inline bool hci_conn_ssp_enabled(struct hci_conn *conn) static inline bool hci_conn_ssp_enabled(struct hci_conn *conn)
...@@ -1310,9 +1313,8 @@ int mgmt_update_adv_data(struct hci_dev *hdev); ...@@ -1310,9 +1313,8 @@ int mgmt_update_adv_data(struct hci_dev *hdev);
void mgmt_discoverable_timeout(struct hci_dev *hdev); void mgmt_discoverable_timeout(struct hci_dev *hdev);
void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
bool persistent); bool persistent);
void mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, void mgmt_device_connected(struct hci_dev *hdev, struct hci_conn *conn,
u8 addr_type, u32 flags, u8 *name, u8 name_len, u32 flags, u8 *name, u8 name_len);
u8 *dev_class);
void mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, void mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 link_type, u8 addr_type, u8 reason, u8 link_type, u8 addr_type, u8 reason,
bool mgmt_connected); bool mgmt_connected);
......
...@@ -10,16 +10,12 @@ ...@@ -10,16 +10,12 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Written by: * Written by:
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
*/ */
#ifndef WPAN_PHY_H #ifndef __NET_CFG802154_H
#define WPAN_PHY_H #define __NET_CFG802154_H
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/mutex.h> #include <linux/mutex.h>
...@@ -61,15 +57,7 @@ struct wpan_phy { ...@@ -61,15 +57,7 @@ struct wpan_phy {
const char *name, int type); const char *name, int type);
void (*del_iface)(struct wpan_phy *phy, struct net_device *dev); void (*del_iface)(struct wpan_phy *phy, struct net_device *dev);
int (*set_txpower)(struct wpan_phy *phy, int db); char priv[0] __aligned(NETDEV_ALIGN);
int (*set_lbt)(struct wpan_phy *phy, bool on);
int (*set_cca_mode)(struct wpan_phy *phy, u8 cca_mode);
int (*set_cca_ed_level)(struct wpan_phy *phy, int level);
int (*set_csma_params)(struct wpan_phy *phy, u8 min_be, u8 max_be,
u8 retries);
int (*set_frame_retries)(struct wpan_phy *phy, s8 retries);
char priv[0] __attribute__((__aligned__(NETDEV_ALIGN)));
}; };
#define to_phy(_dev) container_of(_dev, struct wpan_phy, dev) #define to_phy(_dev) container_of(_dev, struct wpan_phy, dev)
...@@ -79,6 +67,7 @@ static inline void wpan_phy_set_dev(struct wpan_phy *phy, struct device *dev) ...@@ -79,6 +67,7 @@ static inline void wpan_phy_set_dev(struct wpan_phy *phy, struct device *dev)
{ {
phy->dev.parent = dev; phy->dev.parent = dev;
} }
int wpan_phy_register(struct wpan_phy *phy); int wpan_phy_register(struct wpan_phy *phy);
void wpan_phy_unregister(struct wpan_phy *phy); void wpan_phy_unregister(struct wpan_phy *phy);
void wpan_phy_free(struct wpan_phy *phy); void wpan_phy_free(struct wpan_phy *phy);
...@@ -102,4 +91,5 @@ static inline const char *wpan_phy_name(struct wpan_phy *phy) ...@@ -102,4 +91,5 @@ static inline const char *wpan_phy_name(struct wpan_phy *phy)
{ {
return dev_name(&phy->dev); return dev_name(&phy->dev);
} }
#endif
#endif /* __NET_CFG802154_H */
...@@ -12,10 +12,6 @@ ...@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Written by: * Written by:
* Pavel Smolenskiy <pavel.smolenskiy@gmail.com> * Pavel Smolenskiy <pavel.smolenskiy@gmail.com>
* Maxim Gorbachyov <maxim.gorbachev@siemens.com> * Maxim Gorbachyov <maxim.gorbachev@siemens.com>
...@@ -27,10 +23,10 @@ ...@@ -27,10 +23,10 @@
#ifndef IEEE802154_NETDEVICE_H #ifndef IEEE802154_NETDEVICE_H
#define IEEE802154_NETDEVICE_H #define IEEE802154_NETDEVICE_H
#include <net/ieee802154.h>
#include <net/af_ieee802154.h> #include <net/af_ieee802154.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/ieee802154.h>
struct ieee802154_sechdr { struct ieee802154_sechdr {
#if defined(__LITTLE_ENDIAN_BITFIELD) #if defined(__LITTLE_ENDIAN_BITFIELD)
......
...@@ -12,9 +12,6 @@ ...@@ -12,9 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/ */
#ifndef NET_MAC802154_H #ifndef NET_MAC802154_H
#define NET_MAC802154_H #define NET_MAC802154_H
...@@ -35,13 +32,13 @@ ...@@ -35,13 +32,13 @@
*/ */
/* indicates that the Short Address changed */ /* indicates that the Short Address changed */
#define IEEE802515_AFILT_SADDR_CHANGED 0x00000001 #define IEEE802154_AFILT_SADDR_CHANGED 0x00000001
/* indicates that the IEEE Address changed */ /* indicates that the IEEE Address changed */
#define IEEE802515_AFILT_IEEEADDR_CHANGED 0x00000002 #define IEEE802154_AFILT_IEEEADDR_CHANGED 0x00000002
/* indicates that the PAN ID changed */ /* indicates that the PAN ID changed */
#define IEEE802515_AFILT_PANID_CHANGED 0x00000004 #define IEEE802154_AFILT_PANID_CHANGED 0x00000004
/* indicates that PAN Coordinator status changed */ /* indicates that PAN Coordinator status changed */
#define IEEE802515_AFILT_PANC_CHANGED 0x00000008 #define IEEE802154_AFILT_PANC_CHANGED 0x00000008
struct ieee802154_hw_addr_filt { struct ieee802154_hw_addr_filt {
__le16 pan_id; /* Each independent PAN selects a unique __le16 pan_id; /* Each independent PAN selects a unique
...@@ -55,7 +52,7 @@ struct ieee802154_hw_addr_filt { ...@@ -55,7 +52,7 @@ struct ieee802154_hw_addr_filt {
u8 pan_coord; u8 pan_coord;
}; };
struct ieee802154_dev { struct ieee802154_hw {
/* filled by the driver */ /* filled by the driver */
int extra_tx_headroom; int extra_tx_headroom;
u32 flags; u32 flags;
...@@ -76,28 +73,43 @@ struct ieee802154_dev { ...@@ -76,28 +73,43 @@ struct ieee802154_dev {
* however, so you are advised to review these flags carefully. * however, so you are advised to review these flags carefully.
*/ */
/* Indicates that receiver omits FCS and xmitter will add FCS on it's own. */ /* Indicates that xmitter will add FCS on it's own. */
#define IEEE802154_HW_OMIT_CKSUM 0x00000001 #define IEEE802154_HW_TX_OMIT_CKSUM 0x00000001
/* Indicates that receiver will autorespond with ACK frames. */ /* Indicates that receiver will autorespond with ACK frames. */
#define IEEE802154_HW_AACK 0x00000002 #define IEEE802154_HW_AACK 0x00000002
/* Indicates that transceiver will support transmit power setting. */ /* Indicates that transceiver will support transmit power setting. */
#define IEEE802154_HW_TXPOWER 0x00000004 #define IEEE802154_HW_TXPOWER 0x00000004
/* Indicates that transceiver will support listen before transmit. */ /* Indicates that transceiver will support listen before transmit. */
#define IEEE802154_HW_LBT 0x00000008 #define IEEE802154_HW_LBT 0x00000008
/* Indicates that transceiver will support cca mode setting. */ /* Indicates that transceiver will support cca mode setting. */
#define IEEE802154_HW_CCA_MODE 0x00000010 #define IEEE802154_HW_CCA_MODE 0x00000010
/* Indicates that transceiver will support cca ed level setting. */ /* Indicates that transceiver will support cca ed level setting. */
#define IEEE802154_HW_CCA_ED_LEVEL 0x00000020 #define IEEE802154_HW_CCA_ED_LEVEL 0x00000020
/* Indicates that transceiver will support csma (max_be, min_be, csma retries) /* Indicates that transceiver will support csma (max_be, min_be, csma retries)
* settings. */ * settings. */
#define IEEE802154_HW_CSMA_PARAMS 0x00000040 #define IEEE802154_HW_CSMA_PARAMS 0x00000040
/* Indicates that transceiver will support ARET frame retries setting. */ /* Indicates that transceiver will support ARET frame retries setting. */
#define IEEE802154_HW_FRAME_RETRIES 0x00000080 #define IEEE802154_HW_FRAME_RETRIES 0x00000080
/* Indicates that transceiver will support hardware address filter setting. */
#define IEEE802154_HW_AFILT 0x00000100
/* Indicates that transceiver will support promiscuous mode setting. */
#define IEEE802154_HW_PROMISCUOUS 0x00000200
/* Indicates that receiver omits FCS. */
#define IEEE802154_HW_RX_OMIT_CKSUM 0x00000400
/* Indicates that receiver will not filter frames with bad checksum. */
#define IEEE802154_HW_RX_DROP_BAD_CKSUM 0x00000800
/* Indicates that receiver omits FCS and xmitter will add FCS on it's own. */
#define IEEE802154_HW_OMIT_CKSUM (IEEE802154_HW_TX_OMIT_CKSUM | \
IEEE802154_HW_RX_OMIT_CKSUM)
/* This groups the most common CSMA support fields into one. */ /* This groups the most common CSMA support fields into one. */
#define IEEE802154_HW_CSMA (IEEE802154_HW_CCA_MODE | \ #define IEEE802154_HW_CSMA (IEEE802154_HW_CCA_MODE | \
IEEE802154_HW_CCA_ED_LEVEL | \ IEEE802154_HW_CCA_ED_LEVEL | \
IEEE802154_HW_CSMA_PARAMS | \ IEEE802154_HW_CSMA_PARAMS)
/* This groups the most common ARET support fields into one. */
#define IEEE802154_HW_ARET (IEEE802154_HW_CSMA | \
IEEE802154_HW_FRAME_RETRIES) IEEE802154_HW_FRAME_RETRIES)
/* struct ieee802154_ops - callbacks from mac802154 to the driver /* struct ieee802154_ops - callbacks from mac802154 to the driver
...@@ -112,12 +124,24 @@ struct ieee802154_dev { ...@@ -112,12 +124,24 @@ struct ieee802154_dev {
* stop: Handler that 802.15.4 module calls for device cleanup. * stop: Handler that 802.15.4 module calls for device cleanup.
* This function is called after the last interface is removed. * This function is called after the last interface is removed.
* *
* xmit: Handler that 802.15.4 module calls for each transmitted frame. * xmit_sync:
* Handler that 802.15.4 module calls for each transmitted frame.
* skb cntains the buffer starting from the IEEE 802.15.4 header.
* The low-level driver should send the frame based on available
* configuration. This is called by a workqueue and useful for
* synchronous 802.15.4 drivers.
* This function should return zero or negative errno.
*
* WARNING:
* This will be deprecated soon. We don't accept synced xmit callbacks
* drivers anymore.
*
* xmit_async:
* Handler that 802.15.4 module calls for each transmitted frame.
* skb cntains the buffer starting from the IEEE 802.15.4 header. * skb cntains the buffer starting from the IEEE 802.15.4 header.
* The low-level driver should send the frame based on available * The low-level driver should send the frame based on available
* configuration. * configuration.
* This function should return zero or negative errno. Called with * This function should return zero or negative errno.
* pib_lock held.
* *
* ed: Handler that 802.15.4 module calls for Energy Detection. * ed: Handler that 802.15.4 module calls for Energy Detection.
* This function should place the value for detected energy * This function should place the value for detected energy
...@@ -159,40 +183,50 @@ struct ieee802154_dev { ...@@ -159,40 +183,50 @@ struct ieee802154_dev {
* set_frame_retries * set_frame_retries
* Sets the retransmission attempt limit. Called with pib_lock held. * Sets the retransmission attempt limit. Called with pib_lock held.
* Returns either zero, or negative errno. * Returns either zero, or negative errno.
*
* set_promiscuous_mode
* Enables or disable promiscuous mode.
*/ */
struct ieee802154_ops { struct ieee802154_ops {
struct module *owner; struct module *owner;
int (*start)(struct ieee802154_dev *dev); int (*start)(struct ieee802154_hw *hw);
void (*stop)(struct ieee802154_dev *dev); void (*stop)(struct ieee802154_hw *hw);
int (*xmit)(struct ieee802154_dev *dev, int (*xmit_sync)(struct ieee802154_hw *hw,
struct sk_buff *skb); struct sk_buff *skb);
int (*ed)(struct ieee802154_dev *dev, u8 *level); int (*xmit_async)(struct ieee802154_hw *hw,
int (*set_channel)(struct ieee802154_dev *dev, struct sk_buff *skb);
int page, int (*ed)(struct ieee802154_hw *hw, u8 *level);
int channel); int (*set_channel)(struct ieee802154_hw *hw, u8 page,
int (*set_hw_addr_filt)(struct ieee802154_dev *dev, u8 channel);
struct ieee802154_hw_addr_filt *filt, int (*set_hw_addr_filt)(struct ieee802154_hw *hw,
struct ieee802154_hw_addr_filt *filt,
unsigned long changed); unsigned long changed);
int (*ieee_addr)(struct ieee802154_dev *dev, __le64 addr); int (*set_txpower)(struct ieee802154_hw *hw, int db);
int (*set_txpower)(struct ieee802154_dev *dev, int db); int (*set_lbt)(struct ieee802154_hw *hw, bool on);
int (*set_lbt)(struct ieee802154_dev *dev, bool on); int (*set_cca_mode)(struct ieee802154_hw *hw, u8 mode);
int (*set_cca_mode)(struct ieee802154_dev *dev, u8 mode); int (*set_cca_ed_level)(struct ieee802154_hw *hw,
int (*set_cca_ed_level)(struct ieee802154_dev *dev,
s32 level); s32 level);
int (*set_csma_params)(struct ieee802154_dev *dev, int (*set_csma_params)(struct ieee802154_hw *hw,
u8 min_be, u8 max_be, u8 retries); u8 min_be, u8 max_be, u8 retries);
int (*set_frame_retries)(struct ieee802154_dev *dev, int (*set_frame_retries)(struct ieee802154_hw *hw,
s8 retries); s8 retries);
int (*set_promiscuous_mode)(struct ieee802154_hw *hw,
const bool on);
}; };
/* Basic interface to register ieee802154 device */ /* Basic interface to register ieee802154 hwice */
struct ieee802154_dev * struct ieee802154_hw *
ieee802154_alloc_device(size_t priv_data_len, struct ieee802154_ops *ops); ieee802154_alloc_hw(size_t priv_data_len, const struct ieee802154_ops *ops);
void ieee802154_free_device(struct ieee802154_dev *dev); void ieee802154_free_hw(struct ieee802154_hw *hw);
int ieee802154_register_device(struct ieee802154_dev *dev); int ieee802154_register_hw(struct ieee802154_hw *hw);
void ieee802154_unregister_device(struct ieee802154_dev *dev); void ieee802154_unregister_hw(struct ieee802154_hw *hw);
void ieee802154_rx_irqsafe(struct ieee802154_dev *dev, struct sk_buff *skb, void ieee802154_rx(struct ieee802154_hw *hw, struct sk_buff *skb);
void ieee802154_rx_irqsafe(struct ieee802154_hw *hw, struct sk_buff *skb,
u8 lqi); u8 lqi);
void ieee802154_wake_queue(struct ieee802154_hw *hw);
void ieee802154_stop_queue(struct ieee802154_hw *hw);
void ieee802154_xmit_complete(struct ieee802154_hw *hw, struct sk_buff *skb);
#endif /* NET_MAC802154_H */ #endif /* NET_MAC802154_H */
...@@ -12,10 +12,6 @@ ...@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/ */
#ifndef IEEE802154_NL_H #ifndef IEEE802154_NL_H
......
...@@ -171,37 +171,6 @@ static int uncompress_context_based_src_addr(struct sk_buff *skb, ...@@ -171,37 +171,6 @@ static int uncompress_context_based_src_addr(struct sk_buff *skb,
return 0; return 0;
} }
static int skb_deliver(struct sk_buff *skb, struct ipv6hdr *hdr,
struct net_device *dev, skb_delivery_cb deliver_skb)
{
struct sk_buff *new;
int stat;
new = skb_copy_expand(skb, sizeof(struct ipv6hdr), skb_tailroom(skb),
GFP_ATOMIC);
kfree_skb(skb);
if (!new)
return -ENOMEM;
skb_push(new, sizeof(struct ipv6hdr));
skb_reset_network_header(new);
skb_copy_to_linear_data(new, hdr, sizeof(struct ipv6hdr));
new->protocol = htons(ETH_P_IPV6);
new->pkt_type = PACKET_HOST;
new->dev = dev;
raw_dump_table(__func__, "raw skb data dump before receiving",
new->data, new->len);
stat = deliver_skb(new, dev);
kfree_skb(new);
return stat;
}
/* Uncompress function for multicast destination address, /* Uncompress function for multicast destination address,
* when M bit is set. * when M bit is set.
*/ */
...@@ -332,10 +301,12 @@ static int uncompress_udp_header(struct sk_buff *skb, struct udphdr *uh) ...@@ -332,10 +301,12 @@ static int uncompress_udp_header(struct sk_buff *skb, struct udphdr *uh)
/* TTL uncompression values */ /* TTL uncompression values */
static const u8 lowpan_ttl_values[] = { 0, 1, 64, 255 }; static const u8 lowpan_ttl_values[] = { 0, 1, 64, 255 };
int lowpan_process_data(struct sk_buff *skb, struct net_device *dev, int
const u8 *saddr, const u8 saddr_type, const u8 saddr_len, lowpan_header_decompress(struct sk_buff *skb, struct net_device *dev,
const u8 *daddr, const u8 daddr_type, const u8 daddr_len, const u8 *saddr, const u8 saddr_type,
u8 iphc0, u8 iphc1, skb_delivery_cb deliver_skb) const u8 saddr_len, const u8 *daddr,
const u8 daddr_type, const u8 daddr_len,
u8 iphc0, u8 iphc1)
{ {
struct ipv6hdr hdr = {}; struct ipv6hdr hdr = {};
u8 tmp, num_context = 0; u8 tmp, num_context = 0;
...@@ -460,7 +431,7 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev, ...@@ -460,7 +431,7 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
/* UDP data uncompression */ /* UDP data uncompression */
if (iphc0 & LOWPAN_IPHC_NH_C) { if (iphc0 & LOWPAN_IPHC_NH_C) {
struct udphdr uh; struct udphdr uh;
struct sk_buff *new; const int needed = sizeof(struct udphdr) + sizeof(hdr);
if (uncompress_udp_header(skb, &uh)) if (uncompress_udp_header(skb, &uh))
goto drop; goto drop;
...@@ -468,14 +439,11 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev, ...@@ -468,14 +439,11 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
/* replace the compressed UDP head by the uncompressed UDP /* replace the compressed UDP head by the uncompressed UDP
* header * header
*/ */
new = skb_copy_expand(skb, sizeof(struct udphdr), err = skb_cow(skb, needed);
skb_tailroom(skb), GFP_ATOMIC); if (unlikely(err)) {
kfree_skb(skb); kfree_skb(skb);
return err;
if (!new) }
return -ENOMEM;
skb = new;
skb_push(skb, sizeof(struct udphdr)); skb_push(skb, sizeof(struct udphdr));
skb_reset_transport_header(skb); skb_reset_transport_header(skb);
...@@ -485,6 +453,12 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev, ...@@ -485,6 +453,12 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
(u8 *)&uh, sizeof(uh)); (u8 *)&uh, sizeof(uh));
hdr.nexthdr = UIP_PROTO_UDP; hdr.nexthdr = UIP_PROTO_UDP;
} else {
err = skb_cow(skb, sizeof(hdr));
if (unlikely(err)) {
kfree_skb(skb);
return err;
}
} }
hdr.payload_len = htons(skb->len); hdr.payload_len = htons(skb->len);
...@@ -497,15 +471,18 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev, ...@@ -497,15 +471,18 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
hdr.version, ntohs(hdr.payload_len), hdr.nexthdr, hdr.version, ntohs(hdr.payload_len), hdr.nexthdr,
hdr.hop_limit, &hdr.daddr); hdr.hop_limit, &hdr.daddr);
raw_dump_table(__func__, "raw header dump", (u8 *)&hdr, sizeof(hdr)); skb_push(skb, sizeof(hdr));
skb_reset_network_header(skb);
skb_copy_to_linear_data(skb, &hdr, sizeof(hdr));
return skb_deliver(skb, &hdr, dev, deliver_skb); raw_dump_table(__func__, "raw header dump", (u8 *)&hdr, sizeof(hdr));
return 0;
drop: drop:
kfree_skb(skb); kfree_skb(skb);
return -EINVAL; return -EINVAL;
} }
EXPORT_SYMBOL_GPL(lowpan_process_data); EXPORT_SYMBOL_GPL(lowpan_header_decompress);
static u8 lowpan_compress_addr_64(u8 **hc_ptr, u8 shift, static u8 lowpan_compress_addr_64(u8 **hc_ptr, u8 shift,
const struct in6_addr *ipaddr, const struct in6_addr *ipaddr,
......
...@@ -53,7 +53,7 @@ struct skb_cb { ...@@ -53,7 +53,7 @@ struct skb_cb {
* The list contains struct lowpan_dev elements. * The list contains struct lowpan_dev elements.
*/ */
static LIST_HEAD(bt_6lowpan_devices); static LIST_HEAD(bt_6lowpan_devices);
static DEFINE_RWLOCK(devices_lock); static DEFINE_SPINLOCK(devices_lock);
/* If psm is set to 0 (default value), then 6lowpan is disabled. /* If psm is set to 0 (default value), then 6lowpan is disabled.
* Other values are used to indicate a Protocol Service Multiplexer * Other values are used to indicate a Protocol Service Multiplexer
...@@ -67,6 +67,7 @@ static struct l2cap_chan *listen_chan; ...@@ -67,6 +67,7 @@ static struct l2cap_chan *listen_chan;
struct lowpan_peer { struct lowpan_peer {
struct list_head list; struct list_head list;
struct rcu_head rcu;
struct l2cap_chan *chan; struct l2cap_chan *chan;
/* peer addresses in various formats */ /* peer addresses in various formats */
...@@ -86,6 +87,13 @@ struct lowpan_dev { ...@@ -86,6 +87,13 @@ struct lowpan_dev {
struct delayed_work notify_peers; struct delayed_work notify_peers;
}; };
static inline void peer_free(struct rcu_head *head)
{
struct lowpan_peer *e = container_of(head, struct lowpan_peer, rcu);
kfree(e);
}
static inline struct lowpan_dev *lowpan_dev(const struct net_device *netdev) static inline struct lowpan_dev *lowpan_dev(const struct net_device *netdev)
{ {
return netdev_priv(netdev); return netdev_priv(netdev);
...@@ -93,13 +101,14 @@ static inline struct lowpan_dev *lowpan_dev(const struct net_device *netdev) ...@@ -93,13 +101,14 @@ static inline struct lowpan_dev *lowpan_dev(const struct net_device *netdev)
static inline void peer_add(struct lowpan_dev *dev, struct lowpan_peer *peer) static inline void peer_add(struct lowpan_dev *dev, struct lowpan_peer *peer)
{ {
list_add(&peer->list, &dev->peers); list_add_rcu(&peer->list, &dev->peers);
atomic_inc(&dev->peer_count); atomic_inc(&dev->peer_count);
} }
static inline bool peer_del(struct lowpan_dev *dev, struct lowpan_peer *peer) static inline bool peer_del(struct lowpan_dev *dev, struct lowpan_peer *peer)
{ {
list_del(&peer->list); list_del_rcu(&peer->list);
call_rcu(&peer->rcu, peer_free);
module_put(THIS_MODULE); module_put(THIS_MODULE);
...@@ -114,31 +123,37 @@ static inline bool peer_del(struct lowpan_dev *dev, struct lowpan_peer *peer) ...@@ -114,31 +123,37 @@ static inline bool peer_del(struct lowpan_dev *dev, struct lowpan_peer *peer)
static inline struct lowpan_peer *peer_lookup_ba(struct lowpan_dev *dev, static inline struct lowpan_peer *peer_lookup_ba(struct lowpan_dev *dev,
bdaddr_t *ba, __u8 type) bdaddr_t *ba, __u8 type)
{ {
struct lowpan_peer *peer, *tmp; struct lowpan_peer *peer;
BT_DBG("peers %d addr %pMR type %d", atomic_read(&dev->peer_count), BT_DBG("peers %d addr %pMR type %d", atomic_read(&dev->peer_count),
ba, type); ba, type);
list_for_each_entry_safe(peer, tmp, &dev->peers, list) { rcu_read_lock();
list_for_each_entry_rcu(peer, &dev->peers, list) {
BT_DBG("dst addr %pMR dst type %d", BT_DBG("dst addr %pMR dst type %d",
&peer->chan->dst, peer->chan->dst_type); &peer->chan->dst, peer->chan->dst_type);
if (bacmp(&peer->chan->dst, ba)) if (bacmp(&peer->chan->dst, ba))
continue; continue;
if (type == peer->chan->dst_type) if (type == peer->chan->dst_type) {
rcu_read_unlock();
return peer; return peer;
}
} }
rcu_read_unlock();
return NULL; return NULL;
} }
static inline struct lowpan_peer *peer_lookup_chan(struct lowpan_dev *dev, static inline struct lowpan_peer *__peer_lookup_chan(struct lowpan_dev *dev,
struct l2cap_chan *chan) struct l2cap_chan *chan)
{ {
struct lowpan_peer *peer, *tmp; struct lowpan_peer *peer;
list_for_each_entry_safe(peer, tmp, &dev->peers, list) { list_for_each_entry_rcu(peer, &dev->peers, list) {
if (peer->chan == chan) if (peer->chan == chan)
return peer; return peer;
} }
...@@ -146,12 +161,12 @@ static inline struct lowpan_peer *peer_lookup_chan(struct lowpan_dev *dev, ...@@ -146,12 +161,12 @@ static inline struct lowpan_peer *peer_lookup_chan(struct lowpan_dev *dev,
return NULL; return NULL;
} }
static inline struct lowpan_peer *peer_lookup_conn(struct lowpan_dev *dev, static inline struct lowpan_peer *__peer_lookup_conn(struct lowpan_dev *dev,
struct l2cap_conn *conn) struct l2cap_conn *conn)
{ {
struct lowpan_peer *peer, *tmp; struct lowpan_peer *peer;
list_for_each_entry_safe(peer, tmp, &dev->peers, list) { list_for_each_entry_rcu(peer, &dev->peers, list) {
if (peer->chan->conn == conn) if (peer->chan->conn == conn)
return peer; return peer;
} }
...@@ -163,7 +178,7 @@ static inline struct lowpan_peer *peer_lookup_dst(struct lowpan_dev *dev, ...@@ -163,7 +178,7 @@ static inline struct lowpan_peer *peer_lookup_dst(struct lowpan_dev *dev,
struct in6_addr *daddr, struct in6_addr *daddr,
struct sk_buff *skb) struct sk_buff *skb)
{ {
struct lowpan_peer *peer, *tmp; struct lowpan_peer *peer;
struct in6_addr *nexthop; struct in6_addr *nexthop;
struct rt6_info *rt = (struct rt6_info *)skb_dst(skb); struct rt6_info *rt = (struct rt6_info *)skb_dst(skb);
int count = atomic_read(&dev->peer_count); int count = atomic_read(&dev->peer_count);
...@@ -174,9 +189,13 @@ static inline struct lowpan_peer *peer_lookup_dst(struct lowpan_dev *dev, ...@@ -174,9 +189,13 @@ static inline struct lowpan_peer *peer_lookup_dst(struct lowpan_dev *dev,
* send the packet. If only one peer exists, then we can send the * send the packet. If only one peer exists, then we can send the
* packet right away. * packet right away.
*/ */
if (count == 1) if (count == 1) {
return list_first_entry(&dev->peers, struct lowpan_peer, rcu_read_lock();
list); peer = list_first_or_null_rcu(&dev->peers, struct lowpan_peer,
list);
rcu_read_unlock();
return peer;
}
if (!rt) { if (!rt) {
nexthop = &lowpan_cb(skb)->gw; nexthop = &lowpan_cb(skb)->gw;
...@@ -195,53 +214,57 @@ static inline struct lowpan_peer *peer_lookup_dst(struct lowpan_dev *dev, ...@@ -195,53 +214,57 @@ static inline struct lowpan_peer *peer_lookup_dst(struct lowpan_dev *dev,
BT_DBG("gw %pI6c", nexthop); BT_DBG("gw %pI6c", nexthop);
list_for_each_entry_safe(peer, tmp, &dev->peers, list) { rcu_read_lock();
list_for_each_entry_rcu(peer, &dev->peers, list) {
BT_DBG("dst addr %pMR dst type %d ip %pI6c", BT_DBG("dst addr %pMR dst type %d ip %pI6c",
&peer->chan->dst, peer->chan->dst_type, &peer->chan->dst, peer->chan->dst_type,
&peer->peer_addr); &peer->peer_addr);
if (!ipv6_addr_cmp(&peer->peer_addr, nexthop)) if (!ipv6_addr_cmp(&peer->peer_addr, nexthop)) {
rcu_read_unlock();
return peer; return peer;
}
} }
rcu_read_unlock();
return NULL; return NULL;
} }
static struct lowpan_peer *lookup_peer(struct l2cap_conn *conn) static struct lowpan_peer *lookup_peer(struct l2cap_conn *conn)
{ {
struct lowpan_dev *entry, *tmp; struct lowpan_dev *entry;
struct lowpan_peer *peer = NULL; struct lowpan_peer *peer = NULL;
unsigned long flags;
read_lock_irqsave(&devices_lock, flags); rcu_read_lock();
list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) { list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
peer = peer_lookup_conn(entry, conn); peer = __peer_lookup_conn(entry, conn);
if (peer) if (peer)
break; break;
} }
read_unlock_irqrestore(&devices_lock, flags); rcu_read_unlock();
return peer; return peer;
} }
static struct lowpan_dev *lookup_dev(struct l2cap_conn *conn) static struct lowpan_dev *lookup_dev(struct l2cap_conn *conn)
{ {
struct lowpan_dev *entry, *tmp; struct lowpan_dev *entry;
struct lowpan_dev *dev = NULL; struct lowpan_dev *dev = NULL;
unsigned long flags;
read_lock_irqsave(&devices_lock, flags); rcu_read_lock();
list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) { list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
if (conn->hcon->hdev == entry->hdev) { if (conn->hcon->hdev == entry->hdev) {
dev = entry; dev = entry;
break; break;
} }
} }
read_unlock_irqrestore(&devices_lock, flags); rcu_read_unlock();
return dev; return dev;
} }
...@@ -249,35 +272,27 @@ static struct lowpan_dev *lookup_dev(struct l2cap_conn *conn) ...@@ -249,35 +272,27 @@ static struct lowpan_dev *lookup_dev(struct l2cap_conn *conn)
static int give_skb_to_upper(struct sk_buff *skb, struct net_device *dev) static int give_skb_to_upper(struct sk_buff *skb, struct net_device *dev)
{ {
struct sk_buff *skb_cp; struct sk_buff *skb_cp;
int ret;
skb_cp = skb_copy(skb, GFP_ATOMIC); skb_cp = skb_copy(skb, GFP_ATOMIC);
if (!skb_cp) if (!skb_cp)
return -ENOMEM;
ret = netif_rx(skb_cp);
if (ret < 0) {
BT_DBG("receive skb %d", ret);
return NET_RX_DROP; return NET_RX_DROP;
}
return ret; return netif_rx(skb_cp);
} }
static int process_data(struct sk_buff *skb, struct net_device *netdev, static int iphc_decompress(struct sk_buff *skb, struct net_device *netdev,
struct l2cap_chan *chan) struct l2cap_chan *chan)
{ {
const u8 *saddr, *daddr; const u8 *saddr, *daddr;
u8 iphc0, iphc1; u8 iphc0, iphc1;
struct lowpan_dev *dev; struct lowpan_dev *dev;
struct lowpan_peer *peer; struct lowpan_peer *peer;
unsigned long flags;
dev = lowpan_dev(netdev); dev = lowpan_dev(netdev);
read_lock_irqsave(&devices_lock, flags); rcu_read_lock();
peer = peer_lookup_chan(dev, chan); peer = __peer_lookup_chan(dev, chan);
read_unlock_irqrestore(&devices_lock, flags); rcu_read_unlock();
if (!peer) if (!peer)
goto drop; goto drop;
...@@ -294,10 +309,11 @@ static int process_data(struct sk_buff *skb, struct net_device *netdev, ...@@ -294,10 +309,11 @@ static int process_data(struct sk_buff *skb, struct net_device *netdev,
if (lowpan_fetch_skb_u8(skb, &iphc1)) if (lowpan_fetch_skb_u8(skb, &iphc1))
goto drop; goto drop;
return lowpan_process_data(skb, netdev, return lowpan_header_decompress(skb, netdev,
saddr, IEEE802154_ADDR_LONG, EUI64_ADDR_LEN, saddr, IEEE802154_ADDR_LONG,
daddr, IEEE802154_ADDR_LONG, EUI64_ADDR_LEN, EUI64_ADDR_LEN, daddr,
iphc0, iphc1, give_skb_to_upper); IEEE802154_ADDR_LONG, EUI64_ADDR_LEN,
iphc0, iphc1);
drop: drop:
kfree_skb(skb); kfree_skb(skb);
...@@ -316,6 +332,10 @@ static int recv_pkt(struct sk_buff *skb, struct net_device *dev, ...@@ -316,6 +332,10 @@ static int recv_pkt(struct sk_buff *skb, struct net_device *dev,
if (dev->type != ARPHRD_6LOWPAN) if (dev->type != ARPHRD_6LOWPAN)
goto drop; goto drop;
skb = skb_share_check(skb, GFP_ATOMIC);
if (!skb)
goto drop;
/* check that it's our buffer */ /* check that it's our buffer */
if (skb->data[0] == LOWPAN_DISPATCH_IPV6) { if (skb->data[0] == LOWPAN_DISPATCH_IPV6) {
/* Copy the packet so that the IPv6 header is /* Copy the packet so that the IPv6 header is
...@@ -340,8 +360,8 @@ static int recv_pkt(struct sk_buff *skb, struct net_device *dev, ...@@ -340,8 +360,8 @@ static int recv_pkt(struct sk_buff *skb, struct net_device *dev,
dev->stats.rx_bytes += skb->len; dev->stats.rx_bytes += skb->len;
dev->stats.rx_packets++; dev->stats.rx_packets++;
kfree_skb(local_skb); consume_skb(local_skb);
kfree_skb(skb); consume_skb(skb);
} else { } else {
switch (skb->data[0] & 0xe0) { switch (skb->data[0] & 0xe0) {
case LOWPAN_DISPATCH_IPHC: /* ipv6 datagram */ case LOWPAN_DISPATCH_IPHC: /* ipv6 datagram */
...@@ -349,14 +369,25 @@ static int recv_pkt(struct sk_buff *skb, struct net_device *dev, ...@@ -349,14 +369,25 @@ static int recv_pkt(struct sk_buff *skb, struct net_device *dev,
if (!local_skb) if (!local_skb)
goto drop; goto drop;
ret = process_data(local_skb, dev, chan); ret = iphc_decompress(local_skb, dev, chan);
if (ret != NET_RX_SUCCESS) if (ret < 0)
goto drop; goto drop;
local_skb->protocol = htons(ETH_P_IPV6);
local_skb->pkt_type = PACKET_HOST;
local_skb->dev = dev;
if (give_skb_to_upper(local_skb, dev)
!= NET_RX_SUCCESS) {
kfree_skb(local_skb);
goto drop;
}
dev->stats.rx_bytes += skb->len; dev->stats.rx_bytes += skb->len;
dev->stats.rx_packets++; dev->stats.rx_packets++;
kfree_skb(skb); consume_skb(local_skb);
consume_skb(skb);
break; break;
default: default:
break; break;
...@@ -443,7 +474,6 @@ static int setup_header(struct sk_buff *skb, struct net_device *netdev, ...@@ -443,7 +474,6 @@ static int setup_header(struct sk_buff *skb, struct net_device *netdev,
if (ipv6_addr_is_multicast(&ipv6_daddr)) { if (ipv6_addr_is_multicast(&ipv6_daddr)) {
lowpan_cb(skb)->chan = NULL; lowpan_cb(skb)->chan = NULL;
} else { } else {
unsigned long flags;
u8 addr_type; u8 addr_type;
/* Get destination BT device from skb. /* Get destination BT device from skb.
...@@ -454,19 +484,14 @@ static int setup_header(struct sk_buff *skb, struct net_device *netdev, ...@@ -454,19 +484,14 @@ static int setup_header(struct sk_buff *skb, struct net_device *netdev,
BT_DBG("dest addr %pMR type %d IP %pI6c", &addr, BT_DBG("dest addr %pMR type %d IP %pI6c", &addr,
addr_type, &ipv6_daddr); addr_type, &ipv6_daddr);
read_lock_irqsave(&devices_lock, flags);
peer = peer_lookup_ba(dev, &addr, addr_type); peer = peer_lookup_ba(dev, &addr, addr_type);
read_unlock_irqrestore(&devices_lock, flags);
if (!peer) { if (!peer) {
/* The packet might be sent to 6lowpan interface /* The packet might be sent to 6lowpan interface
* because of routing (either via default route * because of routing (either via default route
* or user set route) so get peer according to * or user set route) so get peer according to
* the destination address. * the destination address.
*/ */
read_lock_irqsave(&devices_lock, flags);
peer = peer_lookup_dst(dev, &ipv6_daddr, skb); peer = peer_lookup_dst(dev, &ipv6_daddr, skb);
read_unlock_irqrestore(&devices_lock, flags);
if (!peer) { if (!peer) {
BT_DBG("no such peer %pMR found", &addr); BT_DBG("no such peer %pMR found", &addr);
return -ENOENT; return -ENOENT;
...@@ -549,14 +574,13 @@ static int send_pkt(struct l2cap_chan *chan, struct sk_buff *skb, ...@@ -549,14 +574,13 @@ static int send_pkt(struct l2cap_chan *chan, struct sk_buff *skb,
static int send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev) static int send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev)
{ {
struct sk_buff *local_skb; struct sk_buff *local_skb;
struct lowpan_dev *entry, *tmp; struct lowpan_dev *entry;
unsigned long flags;
int err = 0; int err = 0;
read_lock_irqsave(&devices_lock, flags); rcu_read_lock();
list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) { list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
struct lowpan_peer *pentry, *ptmp; struct lowpan_peer *pentry;
struct lowpan_dev *dev; struct lowpan_dev *dev;
if (entry->netdev != netdev) if (entry->netdev != netdev)
...@@ -564,7 +588,7 @@ static int send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev) ...@@ -564,7 +588,7 @@ static int send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev)
dev = lowpan_dev(entry->netdev); dev = lowpan_dev(entry->netdev);
list_for_each_entry_safe(pentry, ptmp, &dev->peers, list) { list_for_each_entry_rcu(pentry, &dev->peers, list) {
int ret; int ret;
local_skb = skb_clone(skb, GFP_ATOMIC); local_skb = skb_clone(skb, GFP_ATOMIC);
...@@ -581,7 +605,7 @@ static int send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev) ...@@ -581,7 +605,7 @@ static int send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev)
} }
} }
read_unlock_irqrestore(&devices_lock, flags); rcu_read_unlock();
return err; return err;
} }
...@@ -638,7 +662,26 @@ static netdev_tx_t bt_xmit(struct sk_buff *skb, struct net_device *netdev) ...@@ -638,7 +662,26 @@ static netdev_tx_t bt_xmit(struct sk_buff *skb, struct net_device *netdev)
return err < 0 ? NET_XMIT_DROP : err; return err < 0 ? NET_XMIT_DROP : err;
} }
static struct lock_class_key bt_tx_busylock;
static struct lock_class_key bt_netdev_xmit_lock_key;
static void bt_set_lockdep_class_one(struct net_device *dev,
struct netdev_queue *txq,
void *_unused)
{
lockdep_set_class(&txq->_xmit_lock, &bt_netdev_xmit_lock_key);
}
static int bt_dev_init(struct net_device *dev)
{
netdev_for_each_tx_queue(dev, bt_set_lockdep_class_one, NULL);
dev->qdisc_tx_busylock = &bt_tx_busylock;
return 0;
}
static const struct net_device_ops netdev_ops = { static const struct net_device_ops netdev_ops = {
.ndo_init = bt_dev_init,
.ndo_start_xmit = bt_xmit, .ndo_start_xmit = bt_xmit,
}; };
...@@ -783,7 +826,6 @@ static struct l2cap_chan *add_peer_chan(struct l2cap_chan *chan, ...@@ -783,7 +826,6 @@ static struct l2cap_chan *add_peer_chan(struct l2cap_chan *chan,
struct lowpan_dev *dev) struct lowpan_dev *dev)
{ {
struct lowpan_peer *peer; struct lowpan_peer *peer;
unsigned long flags;
peer = kzalloc(sizeof(*peer), GFP_ATOMIC); peer = kzalloc(sizeof(*peer), GFP_ATOMIC);
if (!peer) if (!peer)
...@@ -806,10 +848,10 @@ static struct l2cap_chan *add_peer_chan(struct l2cap_chan *chan, ...@@ -806,10 +848,10 @@ static struct l2cap_chan *add_peer_chan(struct l2cap_chan *chan,
*/ */
set_ip_addr_bits(chan->dst_type, (u8 *)&peer->peer_addr.s6_addr + 8); set_ip_addr_bits(chan->dst_type, (u8 *)&peer->peer_addr.s6_addr + 8);
write_lock_irqsave(&devices_lock, flags); spin_lock(&devices_lock);
INIT_LIST_HEAD(&peer->list); INIT_LIST_HEAD(&peer->list);
peer_add(dev, peer); peer_add(dev, peer);
write_unlock_irqrestore(&devices_lock, flags); spin_unlock(&devices_lock);
/* Notifying peers about us needs to be done without locks held */ /* Notifying peers about us needs to be done without locks held */
INIT_DELAYED_WORK(&dev->notify_peers, do_notify_peers); INIT_DELAYED_WORK(&dev->notify_peers, do_notify_peers);
...@@ -822,7 +864,6 @@ static int setup_netdev(struct l2cap_chan *chan, struct lowpan_dev **dev) ...@@ -822,7 +864,6 @@ static int setup_netdev(struct l2cap_chan *chan, struct lowpan_dev **dev)
{ {
struct net_device *netdev; struct net_device *netdev;
int err = 0; int err = 0;
unsigned long flags;
netdev = alloc_netdev(sizeof(struct lowpan_dev), IFACE_NAME_TEMPLATE, netdev = alloc_netdev(sizeof(struct lowpan_dev), IFACE_NAME_TEMPLATE,
NET_NAME_UNKNOWN, netdev_setup); NET_NAME_UNKNOWN, netdev_setup);
...@@ -852,10 +893,10 @@ static int setup_netdev(struct l2cap_chan *chan, struct lowpan_dev **dev) ...@@ -852,10 +893,10 @@ static int setup_netdev(struct l2cap_chan *chan, struct lowpan_dev **dev)
(*dev)->hdev = chan->conn->hcon->hdev; (*dev)->hdev = chan->conn->hcon->hdev;
INIT_LIST_HEAD(&(*dev)->peers); INIT_LIST_HEAD(&(*dev)->peers);
write_lock_irqsave(&devices_lock, flags); spin_lock(&devices_lock);
INIT_LIST_HEAD(&(*dev)->list); INIT_LIST_HEAD(&(*dev)->list);
list_add(&(*dev)->list, &bt_6lowpan_devices); list_add_rcu(&(*dev)->list, &bt_6lowpan_devices);
write_unlock_irqrestore(&devices_lock, flags); spin_unlock(&devices_lock);
return 0; return 0;
...@@ -909,11 +950,10 @@ static void delete_netdev(struct work_struct *work) ...@@ -909,11 +950,10 @@ static void delete_netdev(struct work_struct *work)
static void chan_close_cb(struct l2cap_chan *chan) static void chan_close_cb(struct l2cap_chan *chan)
{ {
struct lowpan_dev *entry, *tmp; struct lowpan_dev *entry;
struct lowpan_dev *dev = NULL; struct lowpan_dev *dev = NULL;
struct lowpan_peer *peer; struct lowpan_peer *peer;
int err = -ENOENT; int err = -ENOENT;
unsigned long flags;
bool last = false, removed = true; bool last = false, removed = true;
BT_DBG("chan %p conn %p", chan, chan->conn); BT_DBG("chan %p conn %p", chan, chan->conn);
...@@ -928,11 +968,11 @@ static void chan_close_cb(struct l2cap_chan *chan) ...@@ -928,11 +968,11 @@ static void chan_close_cb(struct l2cap_chan *chan)
removed = false; removed = false;
} }
write_lock_irqsave(&devices_lock, flags); spin_lock(&devices_lock);
list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) { list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
dev = lowpan_dev(entry->netdev); dev = lowpan_dev(entry->netdev);
peer = peer_lookup_chan(dev, chan); peer = __peer_lookup_chan(dev, chan);
if (peer) { if (peer) {
last = peer_del(dev, peer); last = peer_del(dev, peer);
err = 0; err = 0;
...@@ -943,13 +983,12 @@ static void chan_close_cb(struct l2cap_chan *chan) ...@@ -943,13 +983,12 @@ static void chan_close_cb(struct l2cap_chan *chan)
atomic_read(&chan->kref.refcount)); atomic_read(&chan->kref.refcount));
l2cap_chan_put(chan); l2cap_chan_put(chan);
kfree(peer);
break; break;
} }
} }
if (!err && last && dev && !atomic_read(&dev->peer_count)) { if (!err && last && dev && !atomic_read(&dev->peer_count)) {
write_unlock_irqrestore(&devices_lock, flags); spin_unlock(&devices_lock);
cancel_delayed_work_sync(&dev->notify_peers); cancel_delayed_work_sync(&dev->notify_peers);
...@@ -960,7 +999,7 @@ static void chan_close_cb(struct l2cap_chan *chan) ...@@ -960,7 +999,7 @@ static void chan_close_cb(struct l2cap_chan *chan)
schedule_work(&entry->delete_netdev); schedule_work(&entry->delete_netdev);
} }
} else { } else {
write_unlock_irqrestore(&devices_lock, flags); spin_unlock(&devices_lock);
} }
return; return;
...@@ -1152,10 +1191,9 @@ static int get_l2cap_conn(char *buf, bdaddr_t *addr, u8 *addr_type, ...@@ -1152,10 +1191,9 @@ static int get_l2cap_conn(char *buf, bdaddr_t *addr, u8 *addr_type,
static void disconnect_all_peers(void) static void disconnect_all_peers(void)
{ {
struct lowpan_dev *entry, *tmp_dev; struct lowpan_dev *entry;
struct lowpan_peer *peer, *tmp_peer, *new_peer; struct lowpan_peer *peer, *tmp_peer, *new_peer;
struct list_head peers; struct list_head peers;
unsigned long flags;
INIT_LIST_HEAD(&peers); INIT_LIST_HEAD(&peers);
...@@ -1164,10 +1202,10 @@ static void disconnect_all_peers(void) ...@@ -1164,10 +1202,10 @@ static void disconnect_all_peers(void)
* with the same list at the same time. * with the same list at the same time.
*/ */
read_lock_irqsave(&devices_lock, flags); rcu_read_lock();
list_for_each_entry_safe(entry, tmp_dev, &bt_6lowpan_devices, list) { list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
list_for_each_entry_safe(peer, tmp_peer, &entry->peers, list) { list_for_each_entry_rcu(peer, &entry->peers, list) {
new_peer = kmalloc(sizeof(*new_peer), GFP_ATOMIC); new_peer = kmalloc(sizeof(*new_peer), GFP_ATOMIC);
if (!new_peer) if (!new_peer)
break; break;
...@@ -1179,26 +1217,36 @@ static void disconnect_all_peers(void) ...@@ -1179,26 +1217,36 @@ static void disconnect_all_peers(void)
} }
} }
read_unlock_irqrestore(&devices_lock, flags); rcu_read_unlock();
spin_lock(&devices_lock);
list_for_each_entry_safe(peer, tmp_peer, &peers, list) { list_for_each_entry_safe(peer, tmp_peer, &peers, list) {
l2cap_chan_close(peer->chan, ENOENT); l2cap_chan_close(peer->chan, ENOENT);
kfree(peer);
list_del_rcu(&peer->list);
call_rcu(&peer->rcu, peer_free);
module_put(THIS_MODULE);
} }
spin_unlock(&devices_lock);
} }
static int lowpan_psm_set(void *data, u64 val) struct set_psm {
{ struct work_struct work;
u16 psm; u16 psm;
};
static void do_psm_set(struct work_struct *work)
{
struct set_psm *set_psm = container_of(work, struct set_psm, work);
psm = val; if (set_psm->psm == 0 || psm_6lowpan != set_psm->psm)
if (psm == 0 || psm_6lowpan != psm)
/* Disconnect existing connections if 6lowpan is /* Disconnect existing connections if 6lowpan is
* disabled (psm = 0), or if psm changes. * disabled (psm = 0), or if psm changes.
*/ */
disconnect_all_peers(); disconnect_all_peers();
psm_6lowpan = psm; psm_6lowpan = set_psm->psm;
if (listen_chan) { if (listen_chan) {
l2cap_chan_close(listen_chan, 0); l2cap_chan_close(listen_chan, 0);
...@@ -1207,6 +1255,22 @@ static int lowpan_psm_set(void *data, u64 val) ...@@ -1207,6 +1255,22 @@ static int lowpan_psm_set(void *data, u64 val)
listen_chan = bt_6lowpan_listen(); listen_chan = bt_6lowpan_listen();
kfree(set_psm);
}
static int lowpan_psm_set(void *data, u64 val)
{
struct set_psm *set_psm;
set_psm = kzalloc(sizeof(*set_psm), GFP_KERNEL);
if (!set_psm)
return -ENOMEM;
set_psm->psm = val;
INIT_WORK(&set_psm->work, do_psm_set);
schedule_work(&set_psm->work);
return 0; return 0;
} }
...@@ -1288,19 +1352,18 @@ static ssize_t lowpan_control_write(struct file *fp, ...@@ -1288,19 +1352,18 @@ static ssize_t lowpan_control_write(struct file *fp,
static int lowpan_control_show(struct seq_file *f, void *ptr) static int lowpan_control_show(struct seq_file *f, void *ptr)
{ {
struct lowpan_dev *entry, *tmp_dev; struct lowpan_dev *entry;
struct lowpan_peer *peer, *tmp_peer; struct lowpan_peer *peer;
unsigned long flags;
read_lock_irqsave(&devices_lock, flags); spin_lock(&devices_lock);
list_for_each_entry_safe(entry, tmp_dev, &bt_6lowpan_devices, list) { list_for_each_entry(entry, &bt_6lowpan_devices, list) {
list_for_each_entry_safe(peer, tmp_peer, &entry->peers, list) list_for_each_entry(peer, &entry->peers, list)
seq_printf(f, "%pMR (type %u)\n", seq_printf(f, "%pMR (type %u)\n",
&peer->chan->dst, peer->chan->dst_type); &peer->chan->dst, peer->chan->dst_type);
} }
read_unlock_irqrestore(&devices_lock, flags); spin_unlock(&devices_lock);
return 0; return 0;
} }
...@@ -1322,7 +1385,6 @@ static void disconnect_devices(void) ...@@ -1322,7 +1385,6 @@ static void disconnect_devices(void)
{ {
struct lowpan_dev *entry, *tmp, *new_dev; struct lowpan_dev *entry, *tmp, *new_dev;
struct list_head devices; struct list_head devices;
unsigned long flags;
INIT_LIST_HEAD(&devices); INIT_LIST_HEAD(&devices);
...@@ -1331,9 +1393,9 @@ static void disconnect_devices(void) ...@@ -1331,9 +1393,9 @@ static void disconnect_devices(void)
* devices list. * devices list.
*/ */
read_lock_irqsave(&devices_lock, flags); rcu_read_lock();
list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) { list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
new_dev = kmalloc(sizeof(*new_dev), GFP_ATOMIC); new_dev = kmalloc(sizeof(*new_dev), GFP_ATOMIC);
if (!new_dev) if (!new_dev)
break; break;
...@@ -1341,10 +1403,10 @@ static void disconnect_devices(void) ...@@ -1341,10 +1403,10 @@ static void disconnect_devices(void)
new_dev->netdev = entry->netdev; new_dev->netdev = entry->netdev;
INIT_LIST_HEAD(&new_dev->list); INIT_LIST_HEAD(&new_dev->list);
list_add(&new_dev->list, &devices); list_add_rcu(&new_dev->list, &devices);
} }
read_unlock_irqrestore(&devices_lock, flags); rcu_read_unlock();
list_for_each_entry_safe(entry, tmp, &devices, list) { list_for_each_entry_safe(entry, tmp, &devices, list) {
ifdown(entry->netdev); ifdown(entry->netdev);
...@@ -1359,17 +1421,15 @@ static int device_event(struct notifier_block *unused, ...@@ -1359,17 +1421,15 @@ static int device_event(struct notifier_block *unused,
unsigned long event, void *ptr) unsigned long event, void *ptr)
{ {
struct net_device *netdev = netdev_notifier_info_to_dev(ptr); struct net_device *netdev = netdev_notifier_info_to_dev(ptr);
struct lowpan_dev *entry, *tmp; struct lowpan_dev *entry;
unsigned long flags;
if (netdev->type != ARPHRD_6LOWPAN) if (netdev->type != ARPHRD_6LOWPAN)
return NOTIFY_DONE; return NOTIFY_DONE;
switch (event) { switch (event) {
case NETDEV_UNREGISTER: case NETDEV_UNREGISTER:
write_lock_irqsave(&devices_lock, flags); spin_lock(&devices_lock);
list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list_for_each_entry(entry, &bt_6lowpan_devices, list) {
list) {
if (entry->netdev == netdev) { if (entry->netdev == netdev) {
BT_DBG("Unregistered netdev %s %p", BT_DBG("Unregistered netdev %s %p",
netdev->name, netdev); netdev->name, netdev);
...@@ -1378,7 +1438,7 @@ static int device_event(struct notifier_block *unused, ...@@ -1378,7 +1438,7 @@ static int device_event(struct notifier_block *unused,
break; break;
} }
} }
write_unlock_irqrestore(&devices_lock, flags); spin_unlock(&devices_lock);
break; break;
} }
......
...@@ -141,10 +141,11 @@ int hci_disconnect(struct hci_conn *conn, __u8 reason) ...@@ -141,10 +141,11 @@ int hci_disconnect(struct hci_conn *conn, __u8 reason)
*/ */
if (conn->type == ACL_LINK && conn->role == HCI_ROLE_MASTER) { if (conn->type == ACL_LINK && conn->role == HCI_ROLE_MASTER) {
struct hci_dev *hdev = conn->hdev; struct hci_dev *hdev = conn->hdev;
struct hci_cp_read_clock_offset cp; struct hci_cp_read_clock_offset clkoff_cp;
cp.handle = cpu_to_le16(conn->handle); clkoff_cp.handle = cpu_to_le16(conn->handle);
hci_send_cmd(hdev, HCI_OP_READ_CLOCK_OFFSET, sizeof(cp), &cp); hci_send_cmd(hdev, HCI_OP_READ_CLOCK_OFFSET, sizeof(clkoff_cp),
&clkoff_cp);
} }
conn->state = BT_DISCONN; conn->state = BT_DISCONN;
...@@ -415,7 +416,7 @@ static void le_conn_timeout(struct work_struct *work) ...@@ -415,7 +416,7 @@ static void le_conn_timeout(struct work_struct *work)
* happen with broken hardware or if low duty cycle was used * happen with broken hardware or if low duty cycle was used
* (which doesn't have a timeout of its own). * (which doesn't have a timeout of its own).
*/ */
if (test_bit(HCI_ADVERTISING, &hdev->dev_flags)) { if (conn->role == HCI_ROLE_SLAVE) {
u8 enable = 0x00; u8 enable = 0x00;
hci_send_cmd(hdev, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), hci_send_cmd(hdev, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable),
&enable); &enable);
...@@ -517,7 +518,7 @@ int hci_conn_del(struct hci_conn *conn) ...@@ -517,7 +518,7 @@ int hci_conn_del(struct hci_conn *conn)
/* Unacked frames */ /* Unacked frames */
hdev->acl_cnt += conn->sent; hdev->acl_cnt += conn->sent;
} else if (conn->type == LE_LINK) { } else if (conn->type == LE_LINK) {
cancel_delayed_work_sync(&conn->le_conn_timeout); cancel_delayed_work(&conn->le_conn_timeout);
if (hdev->le_pkts) if (hdev->le_pkts)
hdev->le_cnt += conn->sent; hdev->le_cnt += conn->sent;
...@@ -544,6 +545,9 @@ int hci_conn_del(struct hci_conn *conn) ...@@ -544,6 +545,9 @@ int hci_conn_del(struct hci_conn *conn)
hci_conn_del_sysfs(conn); hci_conn_del_sysfs(conn);
if (test_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags))
hci_conn_params_del(conn->hdev, &conn->dst, conn->dst_type);
hci_dev_put(hdev); hci_dev_put(hdev);
hci_conn_put(conn); hci_conn_put(conn);
......
...@@ -4477,7 +4477,7 @@ int hci_req_run(struct hci_request *req, hci_req_complete_t complete) ...@@ -4477,7 +4477,7 @@ int hci_req_run(struct hci_request *req, hci_req_complete_t complete)
BT_DBG("length %u", skb_queue_len(&req->cmd_q)); BT_DBG("length %u", skb_queue_len(&req->cmd_q));
/* If an error occured during request building, remove all HCI /* If an error occurred during request building, remove all HCI
* commands queued on the HCI request queue. * commands queued on the HCI request queue.
*/ */
if (req->err) { if (req->err) {
...@@ -4546,7 +4546,7 @@ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, ...@@ -4546,7 +4546,7 @@ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen,
return -ENOMEM; return -ENOMEM;
} }
/* Stand-alone HCI commands must be flaged as /* Stand-alone HCI commands must be flagged as
* single-command requests. * single-command requests.
*/ */
bt_cb(skb)->req.start = true; bt_cb(skb)->req.start = true;
...@@ -4566,7 +4566,7 @@ void hci_req_add_ev(struct hci_request *req, u16 opcode, u32 plen, ...@@ -4566,7 +4566,7 @@ void hci_req_add_ev(struct hci_request *req, u16 opcode, u32 plen,
BT_DBG("%s opcode 0x%4.4x plen %d", hdev->name, opcode, plen); BT_DBG("%s opcode 0x%4.4x plen %d", hdev->name, opcode, plen);
/* If an error occured during request building, there is no point in /* If an error occurred during request building, there is no point in
* queueing the HCI command. We can simply return. * queueing the HCI command. We can simply return.
*/ */
if (req->err) if (req->err)
...@@ -4661,8 +4661,12 @@ static void hci_queue_acl(struct hci_chan *chan, struct sk_buff_head *queue, ...@@ -4661,8 +4661,12 @@ static void hci_queue_acl(struct hci_chan *chan, struct sk_buff_head *queue,
skb_shinfo(skb)->frag_list = NULL; skb_shinfo(skb)->frag_list = NULL;
/* Queue all fragments atomically */ /* Queue all fragments atomically. We need to use spin_lock_bh
spin_lock(&queue->lock); * here because of 6LoWPAN links, as there this function is
* called from softirq and using normal spin lock could cause
* deadlocks.
*/
spin_lock_bh(&queue->lock);
__skb_queue_tail(queue, skb); __skb_queue_tail(queue, skb);
...@@ -4679,7 +4683,7 @@ static void hci_queue_acl(struct hci_chan *chan, struct sk_buff_head *queue, ...@@ -4679,7 +4683,7 @@ static void hci_queue_acl(struct hci_chan *chan, struct sk_buff_head *queue,
__skb_queue_tail(queue, skb); __skb_queue_tail(queue, skb);
} while (list); } while (list);
spin_unlock(&queue->lock); spin_unlock_bh(&queue->lock);
} }
} }
......
...@@ -205,6 +205,8 @@ static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb) ...@@ -205,6 +205,8 @@ static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb)
hdev->le_scan_type = LE_SCAN_PASSIVE; hdev->le_scan_type = LE_SCAN_PASSIVE;
hdev->ssp_debug_mode = 0; hdev->ssp_debug_mode = 0;
hci_bdaddr_list_clear(&hdev->le_white_list);
} }
static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb) static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb)
...@@ -1045,7 +1047,7 @@ static void hci_cc_le_set_adv_enable(struct hci_dev *hdev, struct sk_buff *skb) ...@@ -1045,7 +1047,7 @@ static void hci_cc_le_set_adv_enable(struct hci_dev *hdev, struct sk_buff *skb)
hci_dev_lock(hdev); hci_dev_lock(hdev);
/* If we're doing connection initation as peripheral. Set a /* If we're doing connection initiation as peripheral. Set a
* timeout in case something goes wrong. * timeout in case something goes wrong.
*/ */
if (*sent) { if (*sent) {
...@@ -1577,8 +1579,7 @@ static void hci_check_pending_name(struct hci_dev *hdev, struct hci_conn *conn, ...@@ -1577,8 +1579,7 @@ static void hci_check_pending_name(struct hci_dev *hdev, struct hci_conn *conn,
struct inquiry_entry *e; struct inquiry_entry *e;
if (conn && !test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) if (conn && !test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
mgmt_device_connected(hdev, bdaddr, ACL_LINK, 0x00, 0, name, mgmt_device_connected(hdev, conn, 0, name, name_len);
name_len, conn->dev_class);
if (discov->state == DISCOVERY_STOPPED) if (discov->state == DISCOVERY_STOPPED)
return; return;
...@@ -2536,9 +2537,7 @@ static void hci_remote_features_evt(struct hci_dev *hdev, ...@@ -2536,9 +2537,7 @@ static void hci_remote_features_evt(struct hci_dev *hdev,
cp.pscan_rep_mode = 0x02; cp.pscan_rep_mode = 0x02;
hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ, sizeof(cp), &cp); hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ, sizeof(cp), &cp);
} else if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) } else if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
mgmt_device_connected(hdev, &conn->dst, conn->type, mgmt_device_connected(hdev, conn, 0, NULL, 0);
conn->dst_type, 0, NULL, 0,
conn->dev_class);
if (!hci_outgoing_auth_needed(hdev, conn)) { if (!hci_outgoing_auth_needed(hdev, conn)) {
conn->state = BT_CONNECTED; conn->state = BT_CONNECTED;
...@@ -3434,9 +3433,7 @@ static void hci_remote_ext_features_evt(struct hci_dev *hdev, ...@@ -3434,9 +3433,7 @@ static void hci_remote_ext_features_evt(struct hci_dev *hdev,
cp.pscan_rep_mode = 0x02; cp.pscan_rep_mode = 0x02;
hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ, sizeof(cp), &cp); hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ, sizeof(cp), &cp);
} else if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) } else if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
mgmt_device_connected(hdev, &conn->dst, conn->type, mgmt_device_connected(hdev, conn, 0, NULL, 0);
conn->dst_type, 0, NULL, 0,
conn->dev_class);
if (!hci_outgoing_auth_needed(hdev, conn)) { if (!hci_outgoing_auth_needed(hdev, conn)) {
conn->state = BT_CONNECTED; conn->state = BT_CONNECTED;
...@@ -4214,8 +4211,7 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) ...@@ -4214,8 +4211,7 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
} }
if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
mgmt_device_connected(hdev, &conn->dst, conn->type, mgmt_device_connected(hdev, conn, 0, NULL, 0);
conn->dst_type, 0, NULL, 0, NULL);
conn->sec_level = BT_SECURITY_LOW; conn->sec_level = BT_SECURITY_LOW;
conn->handle = __le16_to_cpu(ev->handle); conn->handle = __le16_to_cpu(ev->handle);
...@@ -4269,25 +4265,26 @@ static void hci_le_conn_update_complete_evt(struct hci_dev *hdev, ...@@ -4269,25 +4265,26 @@ static void hci_le_conn_update_complete_evt(struct hci_dev *hdev,
} }
/* This function requires the caller holds hdev->lock */ /* This function requires the caller holds hdev->lock */
static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr, static struct hci_conn *check_pending_le_conn(struct hci_dev *hdev,
u8 addr_type, u8 adv_type) bdaddr_t *addr,
u8 addr_type, u8 adv_type)
{ {
struct hci_conn *conn; struct hci_conn *conn;
struct hci_conn_params *params; struct hci_conn_params *params;
/* If the event is not connectable don't proceed further */ /* If the event is not connectable don't proceed further */
if (adv_type != LE_ADV_IND && adv_type != LE_ADV_DIRECT_IND) if (adv_type != LE_ADV_IND && adv_type != LE_ADV_DIRECT_IND)
return; return NULL;
/* Ignore if the device is blocked */ /* Ignore if the device is blocked */
if (hci_bdaddr_list_lookup(&hdev->blacklist, addr, addr_type)) if (hci_bdaddr_list_lookup(&hdev->blacklist, addr, addr_type))
return; return NULL;
/* Most controller will fail if we try to create new connections /* Most controller will fail if we try to create new connections
* while we have an existing one in slave role. * while we have an existing one in slave role.
*/ */
if (hdev->conn_hash.le_num_slave > 0) if (hdev->conn_hash.le_num_slave > 0)
return; return NULL;
/* If we're not connectable only connect devices that we have in /* If we're not connectable only connect devices that we have in
* our pend_le_conns list. * our pend_le_conns list.
...@@ -4295,7 +4292,7 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr, ...@@ -4295,7 +4292,7 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr,
params = hci_pend_le_action_lookup(&hdev->pend_le_conns, params = hci_pend_le_action_lookup(&hdev->pend_le_conns,
addr, addr_type); addr, addr_type);
if (!params) if (!params)
return; return NULL;
switch (params->auto_connect) { switch (params->auto_connect) {
case HCI_AUTO_CONN_DIRECT: case HCI_AUTO_CONN_DIRECT:
...@@ -4304,7 +4301,7 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr, ...@@ -4304,7 +4301,7 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr,
* incoming connections from slave devices. * incoming connections from slave devices.
*/ */
if (adv_type != LE_ADV_DIRECT_IND) if (adv_type != LE_ADV_DIRECT_IND)
return; return NULL;
break; break;
case HCI_AUTO_CONN_ALWAYS: case HCI_AUTO_CONN_ALWAYS:
/* Devices advertising with ADV_IND or ADV_DIRECT_IND /* Devices advertising with ADV_IND or ADV_DIRECT_IND
...@@ -4315,7 +4312,7 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr, ...@@ -4315,7 +4312,7 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr,
*/ */
break; break;
default: default:
return; return NULL;
} }
conn = hci_connect_le(hdev, addr, addr_type, BT_SECURITY_LOW, conn = hci_connect_le(hdev, addr, addr_type, BT_SECURITY_LOW,
...@@ -4328,7 +4325,7 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr, ...@@ -4328,7 +4325,7 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr,
* count consistent once the connection is established. * count consistent once the connection is established.
*/ */
params->conn = hci_conn_get(conn); params->conn = hci_conn_get(conn);
return; return conn;
} }
switch (PTR_ERR(conn)) { switch (PTR_ERR(conn)) {
...@@ -4341,7 +4338,10 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr, ...@@ -4341,7 +4338,10 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr,
break; break;
default: default:
BT_DBG("Failed to connect: err %ld", PTR_ERR(conn)); BT_DBG("Failed to connect: err %ld", PTR_ERR(conn));
return NULL;
} }
return NULL;
} }
static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
...@@ -4349,6 +4349,7 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, ...@@ -4349,6 +4349,7 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
{ {
struct discovery_state *d = &hdev->discovery; struct discovery_state *d = &hdev->discovery;
struct smp_irk *irk; struct smp_irk *irk;
struct hci_conn *conn;
bool match; bool match;
u32 flags; u32 flags;
...@@ -4360,7 +4361,14 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, ...@@ -4360,7 +4361,14 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
} }
/* Check if we have been requested to connect to this device */ /* Check if we have been requested to connect to this device */
check_pending_le_conn(hdev, bdaddr, bdaddr_type, type); conn = check_pending_le_conn(hdev, bdaddr, bdaddr_type, type);
if (conn && type == LE_ADV_IND) {
/* Store report for later inclusion by
* mgmt_device_connected
*/
memcpy(conn->le_adv_data, data, len);
conn->le_adv_data_len = len;
}
/* Passive scanning shouldn't trigger any device found events, /* Passive scanning shouldn't trigger any device found events,
* except for devices marked as CONN_REPORT for which we do send * except for devices marked as CONN_REPORT for which we do send
......
...@@ -987,7 +987,7 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock, ...@@ -987,7 +987,7 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
skb_queue_tail(&hdev->raw_q, skb); skb_queue_tail(&hdev->raw_q, skb);
queue_work(hdev->workqueue, &hdev->tx_work); queue_work(hdev->workqueue, &hdev->tx_work);
} else { } else {
/* Stand-alone HCI commands must be flaged as /* Stand-alone HCI commands must be flagged as
* single-command requests. * single-command requests.
*/ */
bt_cb(skb)->req.start = true; bt_cb(skb)->req.start = true;
......
...@@ -3873,9 +3873,7 @@ static int l2cap_connect_req(struct l2cap_conn *conn, ...@@ -3873,9 +3873,7 @@ static int l2cap_connect_req(struct l2cap_conn *conn,
hci_dev_lock(hdev); hci_dev_lock(hdev);
if (test_bit(HCI_MGMT, &hdev->dev_flags) && if (test_bit(HCI_MGMT, &hdev->dev_flags) &&
!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &hcon->flags)) !test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &hcon->flags))
mgmt_device_connected(hdev, &hcon->dst, hcon->type, mgmt_device_connected(hdev, hcon, 0, NULL, 0);
hcon->dst_type, 0, NULL, 0,
hcon->dev_class);
hci_dev_unlock(hdev); hci_dev_unlock(hdev);
l2cap_connect(conn, cmd, data, L2CAP_CONN_RSP, 0); l2cap_connect(conn, cmd, data, L2CAP_CONN_RSP, 0);
...@@ -4084,7 +4082,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, ...@@ -4084,7 +4082,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn,
chan->num_conf_req++; chan->num_conf_req++;
} }
/* Got Conf Rsp PENDING from remote side and asume we sent /* Got Conf Rsp PENDING from remote side and assume we sent
Conf Rsp PENDING in the code above */ Conf Rsp PENDING in the code above */
if (test_bit(CONF_REM_CONF_PEND, &chan->conf_state) && if (test_bit(CONF_REM_CONF_PEND, &chan->conf_state) &&
test_bit(CONF_LOC_CONF_PEND, &chan->conf_state)) { test_bit(CONF_LOC_CONF_PEND, &chan->conf_state)) {
...@@ -5494,6 +5492,7 @@ static inline int l2cap_le_credits(struct l2cap_conn *conn, ...@@ -5494,6 +5492,7 @@ static inline int l2cap_le_credits(struct l2cap_conn *conn,
if (credits > max_credits) { if (credits > max_credits) {
BT_ERR("LE credits overflow"); BT_ERR("LE credits overflow");
l2cap_send_disconn_req(chan, ECONNRESET); l2cap_send_disconn_req(chan, ECONNRESET);
l2cap_chan_unlock(chan);
/* Return 0 so that we don't trigger an unnecessary /* Return 0 so that we don't trigger an unnecessary
* command reject packet. * command reject packet.
......
...@@ -2725,10 +2725,40 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data, ...@@ -2725,10 +2725,40 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data,
} }
if (cp->addr.type == BDADDR_BREDR) { if (cp->addr.type == BDADDR_BREDR) {
/* If disconnection is requested, then look up the
* connection. If the remote device is connected, it
* will be later used to terminate the link.
*
* Setting it to NULL explicitly will cause no
* termination of the link.
*/
if (cp->disconnect)
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
&cp->addr.bdaddr);
else
conn = NULL;
err = hci_remove_link_key(hdev, &cp->addr.bdaddr); err = hci_remove_link_key(hdev, &cp->addr.bdaddr);
} else { } else {
u8 addr_type; u8 addr_type;
conn = hci_conn_hash_lookup_ba(hdev, LE_LINK,
&cp->addr.bdaddr);
if (conn) {
/* Defer clearing up the connection parameters
* until closing to give a chance of keeping
* them if a repairing happens.
*/
set_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags);
/* If disconnection is not requested, then
* clear the connection variable so that the
* link is not terminated.
*/
if (!cp->disconnect)
conn = NULL;
}
if (cp->addr.type == BDADDR_LE_PUBLIC) if (cp->addr.type == BDADDR_LE_PUBLIC)
addr_type = ADDR_LE_DEV_PUBLIC; addr_type = ADDR_LE_DEV_PUBLIC;
else else
...@@ -2736,8 +2766,6 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data, ...@@ -2736,8 +2766,6 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data,
hci_remove_irk(hdev, &cp->addr.bdaddr, addr_type); hci_remove_irk(hdev, &cp->addr.bdaddr, addr_type);
hci_conn_params_del(hdev, &cp->addr.bdaddr, addr_type);
err = hci_remove_ltk(hdev, &cp->addr.bdaddr, addr_type); err = hci_remove_ltk(hdev, &cp->addr.bdaddr, addr_type);
} }
...@@ -2747,17 +2775,9 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data, ...@@ -2747,17 +2775,9 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data,
goto unlock; goto unlock;
} }
if (cp->disconnect) { /* If the connection variable is set, then termination of the
if (cp->addr.type == BDADDR_BREDR) * link is requested.
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, */
&cp->addr.bdaddr);
else
conn = hci_conn_hash_lookup_ba(hdev, LE_LINK,
&cp->addr.bdaddr);
} else {
conn = NULL;
}
if (!conn) { if (!conn) {
err = cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE, 0, err = cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE, 0,
&rp, sizeof(rp)); &rp, sizeof(rp));
...@@ -3062,6 +3082,11 @@ static void pairing_complete(struct pending_cmd *cmd, u8 status) ...@@ -3062,6 +3082,11 @@ static void pairing_complete(struct pending_cmd *cmd, u8 status)
hci_conn_put(conn); hci_conn_put(conn);
mgmt_pending_remove(cmd); mgmt_pending_remove(cmd);
/* The device is paired so there is no need to remove
* its connection parameters anymore.
*/
clear_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags);
} }
void mgmt_smp_complete(struct hci_conn *conn, bool complete) void mgmt_smp_complete(struct hci_conn *conn, bool complete)
...@@ -6171,26 +6196,36 @@ static inline u16 eir_append_data(u8 *eir, u16 eir_len, u8 type, u8 *data, ...@@ -6171,26 +6196,36 @@ static inline u16 eir_append_data(u8 *eir, u16 eir_len, u8 type, u8 *data,
return eir_len; return eir_len;
} }
void mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, void mgmt_device_connected(struct hci_dev *hdev, struct hci_conn *conn,
u8 addr_type, u32 flags, u8 *name, u8 name_len, u32 flags, u8 *name, u8 name_len)
u8 *dev_class)
{ {
char buf[512]; char buf[512];
struct mgmt_ev_device_connected *ev = (void *) buf; struct mgmt_ev_device_connected *ev = (void *) buf;
u16 eir_len = 0; u16 eir_len = 0;
bacpy(&ev->addr.bdaddr, bdaddr); bacpy(&ev->addr.bdaddr, &conn->dst);
ev->addr.type = link_to_bdaddr(link_type, addr_type); ev->addr.type = link_to_bdaddr(conn->type, conn->dst_type);
ev->flags = __cpu_to_le32(flags); ev->flags = __cpu_to_le32(flags);
if (name_len > 0) /* We must ensure that the EIR Data fields are ordered and
eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE, * unique. Keep it simple for now and avoid the problem by not
name, name_len); * adding any BR/EDR data to the LE adv.
*/
if (conn->le_adv_data_len > 0) {
memcpy(&ev->eir[eir_len],
conn->le_adv_data, conn->le_adv_data_len);
eir_len = conn->le_adv_data_len;
} else {
if (name_len > 0)
eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE,
name, name_len);
if (dev_class && memcmp(dev_class, "\0\0\0", 3) != 0) if (memcmp(conn->dev_class, "\0\0\0", 3) != 0)
eir_len = eir_append_data(ev->eir, eir_len, eir_len = eir_append_data(ev->eir, eir_len,
EIR_CLASS_OF_DEV, dev_class, 3); EIR_CLASS_OF_DEV,
conn->dev_class, 3);
}
ev->eir_len = cpu_to_le16(eir_len); ev->eir_len = cpu_to_le16(eir_len);
......
...@@ -78,8 +78,8 @@ static struct rfcomm_session *rfcomm_session_del(struct rfcomm_session *s); ...@@ -78,8 +78,8 @@ static struct rfcomm_session *rfcomm_session_del(struct rfcomm_session *s);
#define __get_type(b) ((b & 0xef)) #define __get_type(b) ((b & 0xef))
#define __test_ea(b) ((b & 0x01)) #define __test_ea(b) ((b & 0x01))
#define __test_cr(b) ((b & 0x02)) #define __test_cr(b) (!!(b & 0x02))
#define __test_pf(b) ((b & 0x10)) #define __test_pf(b) (!!(b & 0x10))
#define __addr(cr, dlci) (((dlci & 0x3f) << 2) | (cr << 1) | 0x01) #define __addr(cr, dlci) (((dlci & 0x3f) << 2) | (cr << 1) | 0x01)
#define __ctrl(type, pf) (((type & 0xef) | (pf << 4))) #define __ctrl(type, pf) (((type & 0xef) | (pf << 4)))
...@@ -904,7 +904,7 @@ static int rfcomm_send_nsc(struct rfcomm_session *s, int cr, u8 type) ...@@ -904,7 +904,7 @@ static int rfcomm_send_nsc(struct rfcomm_session *s, int cr, u8 type)
hdr->len = __len8(sizeof(*mcc) + 1); hdr->len = __len8(sizeof(*mcc) + 1);
mcc = (void *) ptr; ptr += sizeof(*mcc); mcc = (void *) ptr; ptr += sizeof(*mcc);
mcc->type = __mcc_type(cr, RFCOMM_NSC); mcc->type = __mcc_type(0, RFCOMM_NSC);
mcc->len = __len8(1); mcc->len = __len8(1);
/* Type that we didn't like */ /* Type that we didn't like */
......
...@@ -191,16 +191,13 @@ int smp_generate_rpa(struct hci_dev *hdev, u8 irk[16], bdaddr_t *rpa) ...@@ -191,16 +191,13 @@ int smp_generate_rpa(struct hci_dev *hdev, u8 irk[16], bdaddr_t *rpa)
return 0; return 0;
} }
static int smp_c1(struct smp_chan *smp, u8 k[16], u8 r[16], u8 preq[7], static int smp_c1(struct crypto_blkcipher *tfm_aes, u8 k[16], u8 r[16],
u8 pres[7], u8 _iat, bdaddr_t *ia, u8 _rat, bdaddr_t *ra, u8 preq[7], u8 pres[7], u8 _iat, bdaddr_t *ia, u8 _rat,
u8 res[16]) bdaddr_t *ra, u8 res[16])
{ {
struct hci_dev *hdev = smp->conn->hcon->hdev;
u8 p1[16], p2[16]; u8 p1[16], p2[16];
int err; int err;
BT_DBG("%s", hdev->name);
memset(p1, 0, 16); memset(p1, 0, 16);
/* p1 = pres || preq || _rat || _iat */ /* p1 = pres || preq || _rat || _iat */
...@@ -218,7 +215,7 @@ static int smp_c1(struct smp_chan *smp, u8 k[16], u8 r[16], u8 preq[7], ...@@ -218,7 +215,7 @@ static int smp_c1(struct smp_chan *smp, u8 k[16], u8 r[16], u8 preq[7],
u128_xor((u128 *) res, (u128 *) r, (u128 *) p1); u128_xor((u128 *) res, (u128 *) r, (u128 *) p1);
/* res = e(k, res) */ /* res = e(k, res) */
err = smp_e(smp->tfm_aes, k, res); err = smp_e(tfm_aes, k, res);
if (err) { if (err) {
BT_ERR("Encrypt data error"); BT_ERR("Encrypt data error");
return err; return err;
...@@ -228,26 +225,23 @@ static int smp_c1(struct smp_chan *smp, u8 k[16], u8 r[16], u8 preq[7], ...@@ -228,26 +225,23 @@ static int smp_c1(struct smp_chan *smp, u8 k[16], u8 r[16], u8 preq[7],
u128_xor((u128 *) res, (u128 *) res, (u128 *) p2); u128_xor((u128 *) res, (u128 *) res, (u128 *) p2);
/* res = e(k, res) */ /* res = e(k, res) */
err = smp_e(smp->tfm_aes, k, res); err = smp_e(tfm_aes, k, res);
if (err) if (err)
BT_ERR("Encrypt data error"); BT_ERR("Encrypt data error");
return err; return err;
} }
static int smp_s1(struct smp_chan *smp, u8 k[16], u8 r1[16], u8 r2[16], static int smp_s1(struct crypto_blkcipher *tfm_aes, u8 k[16], u8 r1[16],
u8 _r[16]) u8 r2[16], u8 _r[16])
{ {
struct hci_dev *hdev = smp->conn->hcon->hdev;
int err; int err;
BT_DBG("%s", hdev->name);
/* Just least significant octets from r1 and r2 are considered */ /* Just least significant octets from r1 and r2 are considered */
memcpy(_r, r2, 8); memcpy(_r, r2, 8);
memcpy(_r + 8, r1, 8); memcpy(_r + 8, r1, 8);
err = smp_e(smp->tfm_aes, k, _r); err = smp_e(tfm_aes, k, _r);
if (err) if (err)
BT_ERR("Encrypt data error"); BT_ERR("Encrypt data error");
...@@ -547,7 +541,7 @@ static u8 smp_confirm(struct smp_chan *smp) ...@@ -547,7 +541,7 @@ static u8 smp_confirm(struct smp_chan *smp)
BT_DBG("conn %p", conn); BT_DBG("conn %p", conn);
ret = smp_c1(smp, smp->tk, smp->prnd, smp->preq, smp->prsp, ret = smp_c1(smp->tfm_aes, smp->tk, smp->prnd, smp->preq, smp->prsp,
conn->hcon->init_addr_type, &conn->hcon->init_addr, conn->hcon->init_addr_type, &conn->hcon->init_addr,
conn->hcon->resp_addr_type, &conn->hcon->resp_addr, conn->hcon->resp_addr_type, &conn->hcon->resp_addr,
cp.confirm_val); cp.confirm_val);
...@@ -578,7 +572,7 @@ static u8 smp_random(struct smp_chan *smp) ...@@ -578,7 +572,7 @@ static u8 smp_random(struct smp_chan *smp)
BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave"); BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave");
ret = smp_c1(smp, smp->tk, smp->rrnd, smp->preq, smp->prsp, ret = smp_c1(smp->tfm_aes, smp->tk, smp->rrnd, smp->preq, smp->prsp,
hcon->init_addr_type, &hcon->init_addr, hcon->init_addr_type, &hcon->init_addr,
hcon->resp_addr_type, &hcon->resp_addr, confirm); hcon->resp_addr_type, &hcon->resp_addr, confirm);
if (ret) if (ret)
...@@ -594,7 +588,7 @@ static u8 smp_random(struct smp_chan *smp) ...@@ -594,7 +588,7 @@ static u8 smp_random(struct smp_chan *smp)
__le64 rand = 0; __le64 rand = 0;
__le16 ediv = 0; __le16 ediv = 0;
smp_s1(smp, smp->tk, smp->rrnd, smp->prnd, stk); smp_s1(smp->tfm_aes, smp->tk, smp->rrnd, smp->prnd, stk);
memset(stk + smp->enc_key_size, 0, memset(stk + smp->enc_key_size, 0,
SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size); SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size);
...@@ -613,7 +607,7 @@ static u8 smp_random(struct smp_chan *smp) ...@@ -613,7 +607,7 @@ static u8 smp_random(struct smp_chan *smp)
smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(smp->prnd), smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(smp->prnd),
smp->prnd); smp->prnd);
smp_s1(smp, smp->tk, smp->prnd, smp->rrnd, stk); smp_s1(smp->tfm_aes, smp->tk, smp->prnd, smp->rrnd, stk);
memset(stk + smp->enc_key_size, 0, memset(stk + smp->enc_key_size, 0,
SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size); SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size);
...@@ -970,7 +964,7 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) ...@@ -970,7 +964,7 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
if (sec_level > conn->hcon->pending_sec_level) if (sec_level > conn->hcon->pending_sec_level)
conn->hcon->pending_sec_level = sec_level; conn->hcon->pending_sec_level = sec_level;
/* If we need MITM check that it can be acheived */ /* If we need MITM check that it can be achieved */
if (conn->hcon->pending_sec_level >= BT_SECURITY_HIGH) { if (conn->hcon->pending_sec_level >= BT_SECURITY_HIGH) {
u8 method; u8 method;
...@@ -1028,7 +1022,7 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) ...@@ -1028,7 +1022,7 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
auth = rsp->auth_req & AUTH_REQ_MASK; auth = rsp->auth_req & AUTH_REQ_MASK;
/* If we need MITM check that it can be acheived */ /* If we need MITM check that it can be achieved */
if (conn->hcon->pending_sec_level >= BT_SECURITY_HIGH) { if (conn->hcon->pending_sec_level >= BT_SECURITY_HIGH) {
u8 method; u8 method;
......
...@@ -49,8 +49,8 @@ ...@@ -49,8 +49,8 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/ieee802154.h>
#include <net/af_ieee802154.h> #include <net/af_ieee802154.h>
#include <net/ieee802154.h>
#include <net/ieee802154_netdev.h> #include <net/ieee802154_netdev.h>
#include <net/6lowpan.h> #include <net/6lowpan.h>
#include <net/ipv6.h> #include <net/ipv6.h>
...@@ -58,12 +58,13 @@ ...@@ -58,12 +58,13 @@
#include "reassembly.h" #include "reassembly.h"
static LIST_HEAD(lowpan_devices); static LIST_HEAD(lowpan_devices);
static int lowpan_open_count;
/* private device info */ /* private device info */
struct lowpan_dev_info { struct lowpan_dev_info {
struct net_device *real_dev; /* real WPAN device ptr */ struct net_device *real_dev; /* real WPAN device ptr */
struct mutex dev_list_mtx; /* mutex for list ops */ struct mutex dev_list_mtx; /* mutex for list ops */
__be16 fragment_tag; u16 fragment_tag;
}; };
struct lowpan_dev_record { struct lowpan_dev_record {
...@@ -140,24 +141,33 @@ static int lowpan_give_skb_to_devices(struct sk_buff *skb, ...@@ -140,24 +141,33 @@ static int lowpan_give_skb_to_devices(struct sk_buff *skb,
struct sk_buff *skb_cp; struct sk_buff *skb_cp;
int stat = NET_RX_SUCCESS; int stat = NET_RX_SUCCESS;
skb->protocol = htons(ETH_P_IPV6);
skb->pkt_type = PACKET_HOST;
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(entry, &lowpan_devices, list) list_for_each_entry_rcu(entry, &lowpan_devices, list)
if (lowpan_dev_info(entry->ldev)->real_dev == skb->dev) { if (lowpan_dev_info(entry->ldev)->real_dev == skb->dev) {
skb_cp = skb_copy(skb, GFP_ATOMIC); skb_cp = skb_copy(skb, GFP_ATOMIC);
if (!skb_cp) { if (!skb_cp) {
stat = -ENOMEM; kfree_skb(skb);
break; rcu_read_unlock();
return NET_RX_DROP;
} }
skb_cp->dev = entry->ldev; skb_cp->dev = entry->ldev;
stat = netif_rx(skb_cp); stat = netif_rx(skb_cp);
if (stat == NET_RX_DROP)
break;
} }
rcu_read_unlock(); rcu_read_unlock();
consume_skb(skb);
return stat; return stat;
} }
static int process_data(struct sk_buff *skb, const struct ieee802154_hdr *hdr) static int
iphc_decompress(struct sk_buff *skb, const struct ieee802154_hdr *hdr)
{ {
u8 iphc0, iphc1; u8 iphc0, iphc1;
struct ieee802154_addr_sa sa, da; struct ieee802154_addr_sa sa, da;
...@@ -187,10 +197,9 @@ static int process_data(struct sk_buff *skb, const struct ieee802154_hdr *hdr) ...@@ -187,10 +197,9 @@ static int process_data(struct sk_buff *skb, const struct ieee802154_hdr *hdr)
else else
dap = &da.hwaddr; dap = &da.hwaddr;
return lowpan_process_data(skb, skb->dev, sap, sa.addr_type, return lowpan_header_decompress(skb, skb->dev, sap, sa.addr_type,
IEEE802154_ADDR_LEN, dap, da.addr_type, IEEE802154_ADDR_LEN, dap, da.addr_type,
IEEE802154_ADDR_LEN, iphc0, iphc1, IEEE802154_ADDR_LEN, iphc0, iphc1);
lowpan_give_skb_to_devices);
drop: drop:
kfree_skb(skb); kfree_skb(skb);
...@@ -233,7 +242,7 @@ lowpan_alloc_frag(struct sk_buff *skb, int size, ...@@ -233,7 +242,7 @@ lowpan_alloc_frag(struct sk_buff *skb, int size,
&master_hdr->source, size); &master_hdr->source, size);
if (rc < 0) { if (rc < 0) {
kfree_skb(frag); kfree_skb(frag);
return ERR_PTR(-rc); return ERR_PTR(rc);
} }
} else { } else {
frag = ERR_PTR(-ENOMEM); frag = ERR_PTR(-ENOMEM);
...@@ -275,7 +284,8 @@ lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *dev, ...@@ -275,7 +284,8 @@ lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *dev,
dgram_size = lowpan_uncompress_size(skb, &dgram_offset) - dgram_size = lowpan_uncompress_size(skb, &dgram_offset) -
skb->mac_len; skb->mac_len;
frag_tag = lowpan_dev_info(dev)->fragment_tag++; frag_tag = htons(lowpan_dev_info(dev)->fragment_tag);
lowpan_dev_info(dev)->fragment_tag++;
frag_hdr[0] = LOWPAN_DISPATCH_FRAG1 | ((dgram_size >> 8) & 0x07); frag_hdr[0] = LOWPAN_DISPATCH_FRAG1 | ((dgram_size >> 8) & 0x07);
frag_hdr[1] = dgram_size & 0xff; frag_hdr[1] = dgram_size & 0xff;
...@@ -294,7 +304,7 @@ lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *dev, ...@@ -294,7 +304,7 @@ lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *dev,
frag_len + skb_network_header_len(skb)); frag_len + skb_network_header_len(skb));
if (rc) { if (rc) {
pr_debug("%s unable to send FRAG1 packet (tag: %d)", pr_debug("%s unable to send FRAG1 packet (tag: %d)",
__func__, frag_tag); __func__, ntohs(frag_tag));
goto err; goto err;
} }
...@@ -315,7 +325,7 @@ lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *dev, ...@@ -315,7 +325,7 @@ lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *dev,
frag_len); frag_len);
if (rc) { if (rc) {
pr_debug("%s unable to send a FRAGN packet. (tag: %d, offset: %d)\n", pr_debug("%s unable to send a FRAGN packet. (tag: %d, offset: %d)\n",
__func__, frag_tag, skb_offset); __func__, ntohs(frag_tag), skb_offset);
goto err; goto err;
} }
} while (skb_unprocessed > frag_cap); } while (skb_unprocessed > frag_cap);
...@@ -515,6 +525,9 @@ static int lowpan_rcv(struct sk_buff *skb, struct net_device *dev, ...@@ -515,6 +525,9 @@ static int lowpan_rcv(struct sk_buff *skb, struct net_device *dev,
if (!netif_running(dev)) if (!netif_running(dev))
goto drop_skb; goto drop_skb;
if (skb->pkt_type == PACKET_OTHERHOST)
goto drop_skb;
if (dev->type != ARPHRD_IEEE802154) if (dev->type != ARPHRD_IEEE802154)
goto drop_skb; goto drop_skb;
...@@ -523,55 +536,67 @@ static int lowpan_rcv(struct sk_buff *skb, struct net_device *dev, ...@@ -523,55 +536,67 @@ static int lowpan_rcv(struct sk_buff *skb, struct net_device *dev,
/* check that it's our buffer */ /* check that it's our buffer */
if (skb->data[0] == LOWPAN_DISPATCH_IPV6) { if (skb->data[0] == LOWPAN_DISPATCH_IPV6) {
skb->protocol = htons(ETH_P_IPV6);
skb->pkt_type = PACKET_HOST;
/* Pull off the 1-byte of 6lowpan header. */ /* Pull off the 1-byte of 6lowpan header. */
skb_pull(skb, 1); skb_pull(skb, 1);
return lowpan_give_skb_to_devices(skb, NULL);
ret = lowpan_give_skb_to_devices(skb, NULL);
if (ret == NET_RX_DROP)
goto drop;
} else { } else {
switch (skb->data[0] & 0xe0) { switch (skb->data[0] & 0xe0) {
case LOWPAN_DISPATCH_IPHC: /* ipv6 datagram */ case LOWPAN_DISPATCH_IPHC: /* ipv6 datagram */
ret = process_data(skb, &hdr); ret = iphc_decompress(skb, &hdr);
if (ret == NET_RX_DROP) if (ret < 0)
goto drop; goto drop;
break;
return lowpan_give_skb_to_devices(skb, NULL);
case LOWPAN_DISPATCH_FRAG1: /* first fragment header */ case LOWPAN_DISPATCH_FRAG1: /* first fragment header */
ret = lowpan_frag_rcv(skb, LOWPAN_DISPATCH_FRAG1); ret = lowpan_frag_rcv(skb, LOWPAN_DISPATCH_FRAG1);
if (ret == 1) { if (ret == 1) {
ret = process_data(skb, &hdr); ret = iphc_decompress(skb, &hdr);
if (ret == NET_RX_DROP) if (ret < 0)
goto drop; goto drop;
return lowpan_give_skb_to_devices(skb, NULL);
} else if (ret == -1) {
return NET_RX_DROP;
} else {
return NET_RX_SUCCESS;
} }
break;
case LOWPAN_DISPATCH_FRAGN: /* next fragments headers */ case LOWPAN_DISPATCH_FRAGN: /* next fragments headers */
ret = lowpan_frag_rcv(skb, LOWPAN_DISPATCH_FRAGN); ret = lowpan_frag_rcv(skb, LOWPAN_DISPATCH_FRAGN);
if (ret == 1) { if (ret == 1) {
ret = process_data(skb, &hdr); ret = iphc_decompress(skb, &hdr);
if (ret == NET_RX_DROP) if (ret < 0)
goto drop; goto drop;
return lowpan_give_skb_to_devices(skb, NULL);
} else if (ret == -1) {
return NET_RX_DROP;
} else {
return NET_RX_SUCCESS;
} }
break;
default: default:
break; break;
} }
} }
return NET_RX_SUCCESS;
drop_skb: drop_skb:
kfree_skb(skb); kfree_skb(skb);
drop: drop:
return NET_RX_DROP; return NET_RX_DROP;
} }
static struct packet_type lowpan_packet_type = {
.type = htons(ETH_P_IEEE802154),
.func = lowpan_rcv,
};
static int lowpan_newlink(struct net *src_net, struct net_device *dev, static int lowpan_newlink(struct net *src_net, struct net_device *dev,
struct nlattr *tb[], struct nlattr *data[]) struct nlattr *tb[], struct nlattr *data[])
{ {
struct net_device *real_dev; struct net_device *real_dev;
struct lowpan_dev_record *entry; struct lowpan_dev_record *entry;
int ret;
ASSERT_RTNL();
pr_debug("adding new link\n"); pr_debug("adding new link\n");
...@@ -606,9 +631,14 @@ static int lowpan_newlink(struct net *src_net, struct net_device *dev, ...@@ -606,9 +631,14 @@ static int lowpan_newlink(struct net *src_net, struct net_device *dev,
list_add_tail(&entry->list, &lowpan_devices); list_add_tail(&entry->list, &lowpan_devices);
mutex_unlock(&lowpan_dev_info(dev)->dev_list_mtx); mutex_unlock(&lowpan_dev_info(dev)->dev_list_mtx);
register_netdevice(dev); ret = register_netdevice(dev);
if (ret >= 0) {
if (!lowpan_open_count)
dev_add_pack(&lowpan_packet_type);
lowpan_open_count++;
}
return 0; return ret;
} }
static void lowpan_dellink(struct net_device *dev, struct list_head *head) static void lowpan_dellink(struct net_device *dev, struct list_head *head)
...@@ -619,6 +649,10 @@ static void lowpan_dellink(struct net_device *dev, struct list_head *head) ...@@ -619,6 +649,10 @@ static void lowpan_dellink(struct net_device *dev, struct list_head *head)
ASSERT_RTNL(); ASSERT_RTNL();
lowpan_open_count--;
if (!lowpan_open_count)
dev_remove_pack(&lowpan_packet_type);
mutex_lock(&lowpan_dev_info(dev)->dev_list_mtx); mutex_lock(&lowpan_dev_info(dev)->dev_list_mtx);
list_for_each_entry_safe(entry, tmp, &lowpan_devices, list) { list_for_each_entry_safe(entry, tmp, &lowpan_devices, list) {
if (entry->ldev == dev) { if (entry->ldev == dev) {
...@@ -681,11 +715,6 @@ static struct notifier_block lowpan_dev_notifier = { ...@@ -681,11 +715,6 @@ static struct notifier_block lowpan_dev_notifier = {
.notifier_call = lowpan_device_event, .notifier_call = lowpan_device_event,
}; };
static struct packet_type lowpan_packet_type = {
.type = htons(ETH_P_IEEE802154),
.func = lowpan_rcv,
};
static int __init lowpan_init_module(void) static int __init lowpan_init_module(void)
{ {
int err = 0; int err = 0;
...@@ -698,8 +727,6 @@ static int __init lowpan_init_module(void) ...@@ -698,8 +727,6 @@ static int __init lowpan_init_module(void)
if (err < 0) if (err < 0)
goto out_frag; goto out_frag;
dev_add_pack(&lowpan_packet_type);
err = register_netdevice_notifier(&lowpan_dev_notifier); err = register_netdevice_notifier(&lowpan_dev_notifier);
if (err < 0) if (err < 0)
goto out_pack; goto out_pack;
...@@ -707,7 +734,6 @@ static int __init lowpan_init_module(void) ...@@ -707,7 +734,6 @@ static int __init lowpan_init_module(void)
return 0; return 0;
out_pack: out_pack:
dev_remove_pack(&lowpan_packet_type);
lowpan_netlink_fini(); lowpan_netlink_fini();
out_frag: out_frag:
lowpan_net_frag_exit(); lowpan_net_frag_exit();
...@@ -719,8 +745,6 @@ static void __exit lowpan_cleanup_module(void) ...@@ -719,8 +745,6 @@ static void __exit lowpan_cleanup_module(void)
{ {
lowpan_netlink_fini(); lowpan_netlink_fini();
dev_remove_pack(&lowpan_packet_type);
lowpan_net_frag_exit(); lowpan_net_frag_exit();
unregister_netdevice_notifier(&lowpan_dev_notifier); unregister_netdevice_notifier(&lowpan_dev_notifier);
......
...@@ -2,8 +2,8 @@ obj-$(CONFIG_IEEE802154) += ieee802154.o af_802154.o ...@@ -2,8 +2,8 @@ obj-$(CONFIG_IEEE802154) += ieee802154.o af_802154.o
obj-$(CONFIG_IEEE802154_6LOWPAN) += ieee802154_6lowpan.o obj-$(CONFIG_IEEE802154_6LOWPAN) += ieee802154_6lowpan.o
ieee802154_6lowpan-y := 6lowpan_rtnl.o reassembly.o ieee802154_6lowpan-y := 6lowpan_rtnl.o reassembly.o
ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o wpan-class.o \ ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o core.o \
header_ops.o header_ops.o sysfs.o
af_802154-y := af_ieee802154.o raw.o dgram.o af_802154-y := af_ieee802154.o raw.o dgram.o
ccflags-y += -D__CHECK_ENDIAN__ ccflags-y += -D__CHECK_ENDIAN__
...@@ -12,10 +12,6 @@ ...@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Written by: * Written by:
* Sergey Lapin <slapin@ossfans.org> * Sergey Lapin <slapin@ossfans.org>
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
......
...@@ -12,10 +12,6 @@ ...@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Written by: * Written by:
* Sergey Lapin <slapin@ossfans.org> * Sergey Lapin <slapin@ossfans.org>
* Maxim Gorbachyov <maxim.gorbachev@siemens.com> * Maxim Gorbachyov <maxim.gorbachev@siemens.com>
......
...@@ -10,10 +10,6 @@ ...@@ -10,10 +10,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/ */
#include <linux/slab.h> #include <linux/slab.h>
...@@ -21,75 +17,10 @@ ...@@ -21,75 +17,10 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/device.h> #include <linux/device.h>
#include <net/wpan-phy.h> #include <net/cfg802154.h>
#include "ieee802154.h" #include "ieee802154.h"
#include "sysfs.h"
#define MASTER_SHOW_COMPLEX(name, format_string, args...) \
static ssize_t name ## _show(struct device *dev, \
struct device_attribute *attr, char *buf) \
{ \
struct wpan_phy *phy = container_of(dev, struct wpan_phy, dev); \
int ret; \
\
mutex_lock(&phy->pib_lock); \
ret = snprintf(buf, PAGE_SIZE, format_string "\n", args); \
mutex_unlock(&phy->pib_lock); \
return ret; \
} \
static DEVICE_ATTR_RO(name);
#define MASTER_SHOW(field, format_string) \
MASTER_SHOW_COMPLEX(field, format_string, phy->field)
MASTER_SHOW(current_channel, "%d");
MASTER_SHOW(current_page, "%d");
MASTER_SHOW(transmit_power, "%d +- 1 dB");
MASTER_SHOW(cca_mode, "%d");
static ssize_t channels_supported_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct wpan_phy *phy = container_of(dev, struct wpan_phy, dev);
int ret;
int i, len = 0;
mutex_lock(&phy->pib_lock);
for (i = 0; i < 32; i++) {
ret = snprintf(buf + len, PAGE_SIZE - len,
"%#09x\n", phy->channels_supported[i]);
if (ret < 0)
break;
len += ret;
}
mutex_unlock(&phy->pib_lock);
return len;
}
static DEVICE_ATTR_RO(channels_supported);
static struct attribute *pmib_attrs[] = {
&dev_attr_current_channel.attr,
&dev_attr_current_page.attr,
&dev_attr_channels_supported.attr,
&dev_attr_transmit_power.attr,
&dev_attr_cca_mode.attr,
NULL,
};
ATTRIBUTE_GROUPS(pmib);
static void wpan_phy_release(struct device *d)
{
struct wpan_phy *phy = container_of(d, struct wpan_phy, dev);
kfree(phy);
}
static struct class wpan_phy_class = {
.name = "ieee802154",
.dev_release = wpan_phy_release,
.dev_groups = pmib_groups,
};
static DEFINE_MUTEX(wpan_phy_mutex); static DEFINE_MUTEX(wpan_phy_mutex);
static int wpan_phy_idx; static int wpan_phy_idx;
...@@ -201,7 +132,7 @@ static int __init wpan_phy_class_init(void) ...@@ -201,7 +132,7 @@ static int __init wpan_phy_class_init(void)
{ {
int rc; int rc;
rc = class_register(&wpan_phy_class); rc = wpan_phy_sysfs_init();
if (rc) if (rc)
goto err; goto err;
...@@ -211,7 +142,7 @@ static int __init wpan_phy_class_init(void) ...@@ -211,7 +142,7 @@ static int __init wpan_phy_class_init(void)
return 0; return 0;
err_nl: err_nl:
class_unregister(&wpan_phy_class); wpan_phy_sysfs_exit();
err: err:
return rc; return rc;
} }
...@@ -220,7 +151,7 @@ subsys_initcall(wpan_phy_class_init); ...@@ -220,7 +151,7 @@ subsys_initcall(wpan_phy_class_init);
static void __exit wpan_phy_class_exit(void) static void __exit wpan_phy_class_exit(void)
{ {
ieee802154_nl_exit(); ieee802154_nl_exit();
class_unregister(&wpan_phy_class); wpan_phy_sysfs_exit();
} }
module_exit(wpan_phy_class_exit); module_exit(wpan_phy_class_exit);
......
...@@ -12,10 +12,6 @@ ...@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Written by: * Written by:
* Sergey Lapin <slapin@ossfans.org> * Sergey Lapin <slapin@ossfans.org>
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
...@@ -27,9 +23,9 @@ ...@@ -27,9 +23,9 @@
#include <linux/if_arp.h> #include <linux/if_arp.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/ieee802154.h>
#include <net/sock.h> #include <net/sock.h>
#include <net/af_ieee802154.h> #include <net/af_ieee802154.h>
#include <net/ieee802154.h>
#include <net/ieee802154_netdev.h> #include <net/ieee802154_netdev.h>
#include <asm/ioctls.h> #include <asm/ioctls.h>
......
...@@ -14,8 +14,9 @@ ...@@ -14,8 +14,9 @@
* Phoebe Buckheister <phoebe.buckheister@itwm.fraunhofer.de> * Phoebe Buckheister <phoebe.buckheister@itwm.fraunhofer.de>
*/ */
#include <linux/ieee802154.h>
#include <net/mac802154.h> #include <net/mac802154.h>
#include <net/ieee802154.h>
#include <net/ieee802154_netdev.h> #include <net/ieee802154_netdev.h>
static int static int
......
...@@ -10,10 +10,6 @@ ...@@ -10,10 +10,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/ */
#ifndef IEEE_802154_LOCAL_H #ifndef IEEE_802154_LOCAL_H
#define IEEE_802154_LOCAL_H #define IEEE_802154_LOCAL_H
......
...@@ -12,10 +12,6 @@ ...@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Written by: * Written by:
* Sergey Lapin <slapin@ossfans.org> * Sergey Lapin <slapin@ossfans.org>
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
......
...@@ -12,10 +12,6 @@ ...@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Written by: * Written by:
* Sergey Lapin <slapin@ossfans.org> * Sergey Lapin <slapin@ossfans.org>
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
...@@ -26,6 +22,7 @@ ...@@ -26,6 +22,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/if_arp.h> #include <linux/if_arp.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/ieee802154.h>
#include <net/netlink.h> #include <net/netlink.h>
#include <net/genetlink.h> #include <net/genetlink.h>
#include <net/sock.h> #include <net/sock.h>
...@@ -33,9 +30,8 @@ ...@@ -33,9 +30,8 @@
#include <linux/export.h> #include <linux/export.h>
#include <net/af_ieee802154.h> #include <net/af_ieee802154.h>
#include <net/nl802154.h> #include <net/nl802154.h>
#include <net/ieee802154.h>
#include <net/ieee802154_netdev.h> #include <net/ieee802154_netdev.h>
#include <net/wpan-phy.h> #include <net/cfg802154.h>
#include "ieee802154.h" #include "ieee802154.h"
...@@ -668,20 +664,6 @@ int ieee802154_set_macparams(struct sk_buff *skb, struct genl_info *info) ...@@ -668,20 +664,6 @@ int ieee802154_set_macparams(struct sk_buff *skb, struct genl_info *info)
phy = ops->get_phy(dev); phy = ops->get_phy(dev);
if ((!phy->set_lbt && info->attrs[IEEE802154_ATTR_LBT_ENABLED]) ||
(!phy->set_cca_mode && info->attrs[IEEE802154_ATTR_CCA_MODE]) ||
(!phy->set_cca_ed_level &&
info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL]) ||
(!phy->set_csma_params &&
(info->attrs[IEEE802154_ATTR_CSMA_RETRIES] ||
info->attrs[IEEE802154_ATTR_CSMA_MIN_BE] ||
info->attrs[IEEE802154_ATTR_CSMA_MAX_BE])) ||
(!phy->set_frame_retries &&
info->attrs[IEEE802154_ATTR_FRAME_RETRIES])) {
rc = -EOPNOTSUPP;
goto out_phy;
}
ops->get_mac_params(dev, &params); ops->get_mac_params(dev, &params);
if (info->attrs[IEEE802154_ATTR_TXPOWER]) if (info->attrs[IEEE802154_ATTR_TXPOWER])
...@@ -712,10 +694,9 @@ int ieee802154_set_macparams(struct sk_buff *skb, struct genl_info *info) ...@@ -712,10 +694,9 @@ int ieee802154_set_macparams(struct sk_buff *skb, struct genl_info *info)
wpan_phy_put(phy); wpan_phy_put(phy);
dev_put(dev); dev_put(dev);
return rc;
out_phy: return 0;
wpan_phy_put(phy);
out: out:
dev_put(dev); dev_put(dev);
return rc; return rc;
......
...@@ -12,10 +12,6 @@ ...@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Written by: * Written by:
* Sergey Lapin <slapin@ossfans.org> * Sergey Lapin <slapin@ossfans.org>
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
...@@ -27,7 +23,7 @@ ...@@ -27,7 +23,7 @@
#include <linux/if_arp.h> #include <linux/if_arp.h>
#include <net/netlink.h> #include <net/netlink.h>
#include <net/genetlink.h> #include <net/genetlink.h>
#include <net/wpan-phy.h> #include <net/cfg802154.h>
#include <net/af_ieee802154.h> #include <net/af_ieee802154.h>
#include <net/ieee802154_netdev.h> #include <net/ieee802154_netdev.h>
#include <net/rtnetlink.h> /* for rtnl_{un,}lock */ #include <net/rtnetlink.h> /* for rtnl_{un,}lock */
......
...@@ -12,10 +12,6 @@ ...@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>
......
...@@ -12,10 +12,6 @@ ...@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Written by: * Written by:
* Sergey Lapin <slapin@ossfans.org> * Sergey Lapin <slapin@ossfans.org>
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
......
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
static const char lowpan_frags_cache_name[] = "lowpan-frags"; static const char lowpan_frags_cache_name[] = "lowpan-frags";
struct lowpan_frag_info { struct lowpan_frag_info {
__be16 d_tag; u16 d_tag;
u16 d_size; u16 d_size;
u8 d_offset; u8 d_offset;
}; };
...@@ -48,7 +48,7 @@ static struct inet_frags lowpan_frags; ...@@ -48,7 +48,7 @@ static struct inet_frags lowpan_frags;
static int lowpan_frag_reasm(struct lowpan_frag_queue *fq, static int lowpan_frag_reasm(struct lowpan_frag_queue *fq,
struct sk_buff *prev, struct net_device *dev); struct sk_buff *prev, struct net_device *dev);
static unsigned int lowpan_hash_frag(__be16 tag, u16 d_size, static unsigned int lowpan_hash_frag(u16 tag, u16 d_size,
const struct ieee802154_addr *saddr, const struct ieee802154_addr *saddr,
const struct ieee802154_addr *daddr) const struct ieee802154_addr *daddr)
{ {
...@@ -330,11 +330,13 @@ static int lowpan_get_frag_info(struct sk_buff *skb, const u8 frag_type, ...@@ -330,11 +330,13 @@ static int lowpan_get_frag_info(struct sk_buff *skb, const u8 frag_type,
{ {
bool fail; bool fail;
u8 pattern = 0, low = 0; u8 pattern = 0, low = 0;
__be16 d_tag = 0;
fail = lowpan_fetch_skb(skb, &pattern, 1); fail = lowpan_fetch_skb(skb, &pattern, 1);
fail |= lowpan_fetch_skb(skb, &low, 1); fail |= lowpan_fetch_skb(skb, &low, 1);
frag_info->d_size = (pattern & 7) << 8 | low; frag_info->d_size = (pattern & 7) << 8 | low;
fail |= lowpan_fetch_skb(skb, &frag_info->d_tag, 2); fail |= lowpan_fetch_skb(skb, &d_tag, 2);
frag_info->d_tag = ntohs(d_tag);
if (frag_type == LOWPAN_DISPATCH_FRAGN) { if (frag_type == LOWPAN_DISPATCH_FRAGN) {
fail |= lowpan_fetch_skb(skb, &frag_info->d_offset, 1); fail |= lowpan_fetch_skb(skb, &frag_info->d_offset, 1);
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
#include <net/inet_frag.h> #include <net/inet_frag.h>
struct lowpan_create_arg { struct lowpan_create_arg {
__be16 tag; u16 tag;
u16 d_size; u16 d_size;
const struct ieee802154_addr *src; const struct ieee802154_addr *src;
const struct ieee802154_addr *dst; const struct ieee802154_addr *dst;
...@@ -15,7 +15,7 @@ struct lowpan_create_arg { ...@@ -15,7 +15,7 @@ struct lowpan_create_arg {
struct lowpan_frag_queue { struct lowpan_frag_queue {
struct inet_frag_queue q; struct inet_frag_queue q;
__be16 tag; u16 tag;
u16 d_size; u16 d_size;
struct ieee802154_addr saddr; struct ieee802154_addr saddr;
struct ieee802154_addr daddr; struct ieee802154_addr daddr;
......
/* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License 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.
*
* Authors:
* Alexander Aring <aar@pengutronix.de>
*
* Based on: net/wireless/sysfs.c
*/
#include <linux/device.h>
#include <net/cfg802154.h>
#define MASTER_SHOW_COMPLEX(name, format_string, args...) \
static ssize_t name ## _show(struct device *dev, \
struct device_attribute *attr, char *buf) \
{ \
struct wpan_phy *phy = container_of(dev, struct wpan_phy, dev); \
int ret; \
\
mutex_lock(&phy->pib_lock); \
ret = snprintf(buf, PAGE_SIZE, format_string "\n", args); \
mutex_unlock(&phy->pib_lock); \
return ret; \
} \
static DEVICE_ATTR_RO(name)
#define MASTER_SHOW(field, format_string) \
MASTER_SHOW_COMPLEX(field, format_string, phy->field)
MASTER_SHOW(current_channel, "%d");
MASTER_SHOW(current_page, "%d");
MASTER_SHOW(transmit_power, "%d +- 1 dB");
MASTER_SHOW(cca_mode, "%d");
static ssize_t channels_supported_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct wpan_phy *phy = container_of(dev, struct wpan_phy, dev);
int ret;
int i, len = 0;
mutex_lock(&phy->pib_lock);
for (i = 0; i < 32; i++) {
ret = snprintf(buf + len, PAGE_SIZE - len,
"%#09x\n", phy->channels_supported[i]);
if (ret < 0)
break;
len += ret;
}
mutex_unlock(&phy->pib_lock);
return len;
}
static DEVICE_ATTR_RO(channels_supported);
static void wpan_phy_release(struct device *d)
{
struct wpan_phy *phy = container_of(d, struct wpan_phy, dev);
kfree(phy);
}
static struct attribute *pmib_attrs[] = {
&dev_attr_current_channel.attr,
&dev_attr_current_page.attr,
&dev_attr_channels_supported.attr,
&dev_attr_transmit_power.attr,
&dev_attr_cca_mode.attr,
NULL,
};
ATTRIBUTE_GROUPS(pmib);
struct class wpan_phy_class = {
.name = "ieee802154",
.dev_release = wpan_phy_release,
.dev_groups = pmib_groups,
};
int wpan_phy_sysfs_init(void)
{
return class_register(&wpan_phy_class);
}
void wpan_phy_sysfs_exit(void)
{
class_unregister(&wpan_phy_class);
}
#ifndef __IEEE802154_SYSFS_H
#define __IEEE802154_SYSFS_H
int wpan_phy_sysfs_init(void);
void wpan_phy_sysfs_exit(void);
extern struct class wpan_phy_class;
#endif /* __IEEE802154_SYSFS_H */
...@@ -16,5 +16,5 @@ config MAC802154 ...@@ -16,5 +16,5 @@ config MAC802154
been tested yet! been tested yet!
If you plan to use HardMAC IEEE 802.15.4 devices, you can If you plan to use HardMAC IEEE 802.15.4 devices, you can
say N here. Alternatievly you can say M to compile it as say N here. Alternatively you can say M to compile it as
module. module.
obj-$(CONFIG_MAC802154) += mac802154.o obj-$(CONFIG_MAC802154) += mac802154.o
mac802154-objs := ieee802154_dev.o rx.o tx.o mac_cmd.o mib.o \ mac802154-objs := main.o rx.o tx.o mac_cmd.o mib.o \
monitor.o wpan.o llsec.o iface.o llsec.o util.o
ccflags-y += -D__CHECK_ENDIAN__ ccflags-y += -D__CHECK_ENDIAN__
#ifndef __MAC802154_DRVIER_OPS
#define __MAC802154_DRIVER_OPS
#include <linux/types.h>
#include <linux/rtnetlink.h>
#include <net/mac802154.h>
#include "ieee802154_i.h"
static inline int
drv_xmit_async(struct ieee802154_local *local, struct sk_buff *skb)
{
return local->ops->xmit_async(&local->hw, skb);
}
static inline int
drv_xmit_sync(struct ieee802154_local *local, struct sk_buff *skb)
{
/* don't allow other operations while sync xmit */
ASSERT_RTNL();
might_sleep();
return local->ops->xmit_sync(&local->hw, skb);
}
static inline int drv_start(struct ieee802154_local *local)
{
might_sleep();
local->started = true;
smp_mb();
return local->ops->start(&local->hw);
}
static inline void drv_stop(struct ieee802154_local *local)
{
might_sleep();
local->ops->stop(&local->hw);
/* sync away all work on the tasklet before clearing started */
tasklet_disable(&local->tasklet);
tasklet_enable(&local->tasklet);
barrier();
local->started = false;
}
static inline int drv_set_channel(struct ieee802154_local *local,
const u8 page, const u8 channel)
{
might_sleep();
return local->ops->set_channel(&local->hw, page, channel);
}
static inline int drv_set_tx_power(struct ieee802154_local *local,
const s8 dbm)
{
might_sleep();
if (!local->ops->set_txpower) {
WARN_ON(1);
return -EOPNOTSUPP;
}
return local->ops->set_txpower(&local->hw, dbm);
}
static inline int drv_set_cca_mode(struct ieee802154_local *local,
const u8 cca_mode)
{
might_sleep();
if (!local->ops->set_cca_mode) {
WARN_ON(1);
return -EOPNOTSUPP;
}
return local->ops->set_cca_mode(&local->hw, cca_mode);
}
static inline int drv_set_lbt_mode(struct ieee802154_local *local,
const bool mode)
{
might_sleep();
if (!local->ops->set_lbt) {
WARN_ON(1);
return -EOPNOTSUPP;
}
return local->ops->set_lbt(&local->hw, mode);
}
static inline int drv_set_cca_ed_level(struct ieee802154_local *local,
const s32 ed_level)
{
might_sleep();
if (!local->ops->set_cca_ed_level) {
WARN_ON(1);
return -EOPNOTSUPP;
}
return local->ops->set_cca_ed_level(&local->hw, ed_level);
}
static inline int drv_set_pan_id(struct ieee802154_local *local,
const __le16 pan_id)
{
struct ieee802154_hw_addr_filt filt;
might_sleep();
if (!local->ops->set_hw_addr_filt) {
WARN_ON(1);
return -EOPNOTSUPP;
}
filt.pan_id = pan_id;
return local->ops->set_hw_addr_filt(&local->hw, &filt,
IEEE802154_AFILT_PANID_CHANGED);
}
static inline int drv_set_extended_addr(struct ieee802154_local *local,
const __le64 extended_addr)
{
struct ieee802154_hw_addr_filt filt;
might_sleep();
if (!local->ops->set_hw_addr_filt) {
WARN_ON(1);
return -EOPNOTSUPP;
}
filt.ieee_addr = extended_addr;
return local->ops->set_hw_addr_filt(&local->hw, &filt,
IEEE802154_AFILT_IEEEADDR_CHANGED);
}
static inline int drv_set_short_addr(struct ieee802154_local *local,
const __le16 short_addr)
{
struct ieee802154_hw_addr_filt filt;
might_sleep();
if (!local->ops->set_hw_addr_filt) {
WARN_ON(1);
return -EOPNOTSUPP;
}
filt.short_addr = short_addr;
return local->ops->set_hw_addr_filt(&local->hw, &filt,
IEEE802154_AFILT_SADDR_CHANGED);
}
static inline int drv_set_pan_coord(struct ieee802154_local *local,
const bool is_coord)
{
struct ieee802154_hw_addr_filt filt;
might_sleep();
if (!local->ops->set_hw_addr_filt) {
WARN_ON(1);
return -EOPNOTSUPP;
}
filt.pan_coord = is_coord;
return local->ops->set_hw_addr_filt(&local->hw, &filt,
IEEE802154_AFILT_PANC_CHANGED);
}
static inline int drv_set_csma_params(struct ieee802154_local *local,
u8 min_be, u8 max_be,
u8 max_csma_backoffs)
{
might_sleep();
if (!local->ops->set_csma_params) {
WARN_ON(1);
return -EOPNOTSUPP;
}
return local->ops->set_csma_params(&local->hw, min_be, max_be,
max_csma_backoffs);
}
static inline int drv_set_max_frame_retries(struct ieee802154_local *local,
s8 max_frame_retries)
{
might_sleep();
if (!local->ops->set_frame_retries) {
WARN_ON(1);
return -EOPNOTSUPP;
}
return local->ops->set_frame_retries(&local->hw, max_frame_retries);
}
static inline int drv_set_promiscuous_mode(struct ieee802154_local *local,
const bool on)
{
might_sleep();
if (!local->ops->set_promiscuous_mode) {
WARN_ON(1);
return -EOPNOTSUPP;
}
return local->ops->set_promiscuous_mode(&local->hw, on);
}
#endif /* __MAC802154_DRVIER_OPS */
此差异已折叠。
...@@ -10,18 +10,14 @@ ...@@ -10,18 +10,14 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Written by: * Written by:
* Pavel Smolenskiy <pavel.smolenskiy@gmail.com> * Pavel Smolenskiy <pavel.smolenskiy@gmail.com>
* Maxim Gorbachyov <maxim.gorbachev@siemens.com> * Maxim Gorbachyov <maxim.gorbachev@siemens.com>
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
* Alexander Smirnov <alex.bluesman.smirnov@gmail.com> * Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
*/ */
#ifndef MAC802154_H #ifndef __IEEE802154_I_H
#define MAC802154_H #define __IEEE802154_I_H
#include <linux/mutex.h> #include <linux/mutex.h>
#include <net/mac802154.h> #include <net/mac802154.h>
...@@ -30,9 +26,9 @@ ...@@ -30,9 +26,9 @@
#include "llsec.h" #include "llsec.h"
/* mac802154 device private data */ /* mac802154 device private data */
struct mac802154_priv { struct ieee802154_local {
struct ieee802154_dev hw; struct ieee802154_hw hw;
struct ieee802154_ops *ops; const struct ieee802154_ops *ops;
/* ieee802154 phy */ /* ieee802154 phy */
struct wpan_phy *phy; struct wpan_phy *phy;
...@@ -46,23 +42,27 @@ struct mac802154_priv { ...@@ -46,23 +42,27 @@ struct mac802154_priv {
* *
* So atomic readers can use any of this protection methods. * So atomic readers can use any of this protection methods.
*/ */
struct list_head slaves; struct list_head interfaces;
struct mutex slaves_mtx; struct mutex iflist_mtx;
/* This one is used for scanning and other jobs not to be interfered /* This one is used for scanning and other jobs not to be interfered
* with serial driver. * with serial driver.
*/ */
struct workqueue_struct *dev_workqueue; struct workqueue_struct *workqueue;
/* SoftMAC device is registered and running. One can add subinterfaces. bool started;
* This flag should be modified under slaves_mtx and RTNL, so you can
* read them using any of protection methods. struct tasklet_struct tasklet;
*/ struct sk_buff_head skb_queue;
bool running;
}; };
#define MAC802154_DEVICE_STOPPED 0x00 enum {
#define MAC802154_DEVICE_RUN 0x01 IEEE802154_RX_MSG = 1,
};
enum ieee802154_sdata_state_bits {
SDATA_STATE_RUNNING,
};
/* Slave interface definition. /* Slave interface definition.
* *
...@@ -70,23 +70,21 @@ struct mac802154_priv { ...@@ -70,23 +70,21 @@ struct mac802154_priv {
* Each ieee802154 device/transceiver may have several slaves and able * Each ieee802154 device/transceiver may have several slaves and able
* to be associated with several networks at the same time. * to be associated with several networks at the same time.
*/ */
struct mac802154_sub_if_data { struct ieee802154_sub_if_data {
struct list_head list; /* the ieee802154_priv->slaves list */ struct list_head list; /* the ieee802154_priv->slaves list */
struct mac802154_priv *hw; struct ieee802154_local *local;
struct net_device *dev; struct net_device *dev;
int type; int type;
bool running; unsigned long state;
spinlock_t mib_lock; spinlock_t mib_lock;
__le16 pan_id; __le16 pan_id;
__le16 short_addr; __le16 short_addr;
__le64 extended_addr; __le64 extended_addr;
bool promisuous_mode;
u8 chan;
u8 page;
struct ieee802154_mac_params mac_params; struct ieee802154_mac_params mac_params;
...@@ -103,24 +101,36 @@ struct mac802154_sub_if_data { ...@@ -103,24 +101,36 @@ struct mac802154_sub_if_data {
struct mac802154_llsec sec; struct mac802154_llsec sec;
}; };
#define mac802154_to_priv(_hw) container_of(_hw, struct mac802154_priv, hw)
#define MAC802154_CHAN_NONE 0xff /* No channel is assigned */ #define MAC802154_CHAN_NONE 0xff /* No channel is assigned */
static inline struct ieee802154_local *
hw_to_local(struct ieee802154_hw *hw)
{
return container_of(hw, struct ieee802154_local, hw);
}
static inline struct ieee802154_sub_if_data *
IEEE802154_DEV_TO_SUB_IF(const struct net_device *dev)
{
return netdev_priv(dev);
}
static inline bool
ieee802154_sdata_running(struct ieee802154_sub_if_data *sdata)
{
return test_bit(SDATA_STATE_RUNNING, &sdata->state);
}
extern struct ieee802154_reduced_mlme_ops mac802154_mlme_reduced; extern struct ieee802154_reduced_mlme_ops mac802154_mlme_reduced;
extern struct ieee802154_mlme_ops mac802154_mlme_wpan; extern struct ieee802154_mlme_ops mac802154_mlme_wpan;
int mac802154_slave_open(struct net_device *dev);
int mac802154_slave_close(struct net_device *dev);
void mac802154_monitors_rx(struct mac802154_priv *priv, struct sk_buff *skb);
void mac802154_monitor_setup(struct net_device *dev); void mac802154_monitor_setup(struct net_device *dev);
netdev_tx_t
ieee802154_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev);
void mac802154_wpans_rx(struct mac802154_priv *priv, struct sk_buff *skb);
void mac802154_wpan_setup(struct net_device *dev); void mac802154_wpan_setup(struct net_device *dev);
netdev_tx_t
netdev_tx_t mac802154_tx(struct mac802154_priv *priv, struct sk_buff *skb, ieee802154_subif_start_xmit(struct sk_buff *skb, struct net_device *dev);
u8 page, u8 chan);
/* MIB callbacks */ /* MIB callbacks */
void mac802154_dev_set_short_addr(struct net_device *dev, __le16 val); void mac802154_dev_set_short_addr(struct net_device *dev, __le16 val);
...@@ -131,11 +141,6 @@ void mac802154_dev_set_pan_id(struct net_device *dev, __le16 val); ...@@ -131,11 +141,6 @@ void mac802154_dev_set_pan_id(struct net_device *dev, __le16 val);
void mac802154_dev_set_page_channel(struct net_device *dev, u8 page, u8 chan); void mac802154_dev_set_page_channel(struct net_device *dev, u8 page, u8 chan);
u8 mac802154_dev_get_dsn(const struct net_device *dev); u8 mac802154_dev_get_dsn(const struct net_device *dev);
int mac802154_set_mac_params(struct net_device *dev,
const struct ieee802154_mac_params *params);
void mac802154_get_mac_params(struct net_device *dev,
struct ieee802154_mac_params *params);
int mac802154_get_params(struct net_device *dev, int mac802154_get_params(struct net_device *dev,
struct ieee802154_llsec_params *params); struct ieee802154_llsec_params *params);
int mac802154_set_params(struct net_device *dev, int mac802154_set_params(struct net_device *dev,
...@@ -169,4 +174,4 @@ void mac802154_get_table(struct net_device *dev, ...@@ -169,4 +174,4 @@ void mac802154_get_table(struct net_device *dev,
struct ieee802154_llsec_table **t); struct ieee802154_llsec_table **t);
void mac802154_unlock_table(struct net_device *dev); void mac802154_unlock_table(struct net_device *dev);
#endif /* MAC802154_H */ #endif /* __IEEE802154_I_H */
...@@ -17,10 +17,10 @@ ...@@ -17,10 +17,10 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/bug.h> #include <linux/bug.h>
#include <linux/completion.h> #include <linux/completion.h>
#include <net/ieee802154.h> #include <linux/ieee802154.h>
#include <crypto/algapi.h> #include <crypto/algapi.h>
#include "mac802154.h" #include "ieee802154_i.h"
#include "llsec.h" #include "llsec.h"
static void llsec_key_put(struct mac802154_llsec_key *key); static void llsec_key_put(struct mac802154_llsec_key *key);
......
...@@ -12,10 +12,6 @@ ...@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Written by: * Written by:
* Sergey Lapin <slapin@ossfans.org> * Sergey Lapin <slapin@ossfans.org>
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
...@@ -24,14 +20,14 @@ ...@@ -24,14 +20,14 @@
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/if_arp.h> #include <linux/if_arp.h>
#include <linux/ieee802154.h>
#include <net/ieee802154.h>
#include <net/ieee802154_netdev.h> #include <net/ieee802154_netdev.h>
#include <net/wpan-phy.h> #include <net/cfg802154.h>
#include <net/mac802154.h> #include <net/mac802154.h>
#include <net/nl802154.h> #include <net/nl802154.h>
#include "mac802154.h" #include "ieee802154_i.h"
static int mac802154_mlme_start_req(struct net_device *dev, static int mac802154_mlme_start_req(struct net_device *dev,
struct ieee802154_addr *addr, struct ieee802154_addr *addr,
...@@ -79,11 +75,33 @@ static int mac802154_mlme_start_req(struct net_device *dev, ...@@ -79,11 +75,33 @@ static int mac802154_mlme_start_req(struct net_device *dev,
static struct wpan_phy *mac802154_get_phy(const struct net_device *dev) static struct wpan_phy *mac802154_get_phy(const struct net_device *dev)
{ {
struct mac802154_sub_if_data *priv = netdev_priv(dev); struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
BUG_ON(dev->type != ARPHRD_IEEE802154); BUG_ON(dev->type != ARPHRD_IEEE802154);
return to_phy(get_device(&priv->hw->phy->dev)); return to_phy(get_device(&sdata->local->phy->dev));
}
static int mac802154_set_mac_params(struct net_device *dev,
const struct ieee802154_mac_params *params)
{
struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
mutex_lock(&sdata->local->iflist_mtx);
sdata->mac_params = *params;
mutex_unlock(&sdata->local->iflist_mtx);
return 0;
}
static void mac802154_get_mac_params(struct net_device *dev,
struct ieee802154_mac_params *params)
{
struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
mutex_lock(&sdata->local->iflist_mtx);
*params = sdata->mac_params;
mutex_unlock(&sdata->local->iflist_mtx);
} }
static struct ieee802154_llsec_ops mac802154_llsec_ops = { static struct ieee802154_llsec_ops mac802154_llsec_ops = {
......
此差异已折叠。
此差异已折叠。
/*
* Copyright 2007, 2008, 2009 Siemens AG
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License 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.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Written by:
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
* Sergey Lapin <slapin@ossfans.org>
* Maxim Gorbachyov <maxim.gorbachev@siemens.com>
* Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
*/
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/if_arp.h>
#include <linux/crc-ccitt.h>
#include <net/ieee802154.h>
#include <net/mac802154.h>
#include <net/netlink.h>
#include <net/wpan-phy.h>
#include <linux/nl802154.h>
#include "mac802154.h"
static netdev_tx_t mac802154_monitor_xmit(struct sk_buff *skb,
struct net_device *dev)
{
struct mac802154_sub_if_data *priv;
u8 chan, page;
priv = netdev_priv(dev);
/* FIXME: locking */
chan = priv->hw->phy->current_channel;
page = priv->hw->phy->current_page;
if (chan == MAC802154_CHAN_NONE) /* not initialized */
return NETDEV_TX_OK;
if (WARN_ON(page >= WPAN_NUM_PAGES) ||
WARN_ON(chan >= WPAN_NUM_CHANNELS))
return NETDEV_TX_OK;
skb->skb_iif = dev->ifindex;
dev->stats.tx_packets++;
dev->stats.tx_bytes += skb->len;
return mac802154_tx(priv->hw, skb, page, chan);
}
void mac802154_monitors_rx(struct mac802154_priv *priv, struct sk_buff *skb)
{
struct sk_buff *skb2;
struct mac802154_sub_if_data *sdata;
u16 crc = crc_ccitt(0, skb->data, skb->len);
u8 *data;
rcu_read_lock();
list_for_each_entry_rcu(sdata, &priv->slaves, list) {
if (sdata->type != IEEE802154_DEV_MONITOR ||
!netif_running(sdata->dev))
continue;
skb2 = skb_clone(skb, GFP_ATOMIC);
skb2->dev = sdata->dev;
skb2->pkt_type = PACKET_HOST;
data = skb_put(skb2, 2);
data[0] = crc & 0xff;
data[1] = crc >> 8;
netif_rx_ni(skb2);
}
rcu_read_unlock();
}
static const struct net_device_ops mac802154_monitor_ops = {
.ndo_open = mac802154_slave_open,
.ndo_stop = mac802154_slave_close,
.ndo_start_xmit = mac802154_monitor_xmit,
};
void mac802154_monitor_setup(struct net_device *dev)
{
struct mac802154_sub_if_data *priv;
dev->addr_len = 0;
dev->hard_header_len = 0;
dev->needed_tailroom = 2; /* room for FCS */
dev->mtu = IEEE802154_MTU;
dev->tx_queue_len = 10;
dev->type = ARPHRD_IEEE802154_MONITOR;
dev->flags = IFF_NOARP | IFF_BROADCAST;
dev->watchdog_timeo = 0;
dev->destructor = free_netdev;
dev->netdev_ops = &mac802154_monitor_ops;
dev->ml_priv = &mac802154_mlme_reduced;
priv = netdev_priv(dev);
priv->type = IEEE802154_DEV_MONITOR;
priv->chan = MAC802154_CHAN_NONE; /* not initialized */
priv->page = 0;
}
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册