Merge branch 'for-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next

This commit is contained in:
John W. Linville
2014-10-31 16:05:31 -04:00
62 changed files with 2547 additions and 2738 deletions

View File

@@ -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
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

View File

@@ -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[] = {
{ 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 },

View File

@@ -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 },

View File

@@ -168,6 +168,36 @@ wakeup:
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)
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) {

View File

@@ -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"

View File

@@ -1,4 +1,3 @@
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

View File

@@ -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:
* 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>
@@ -33,10 +29,10 @@
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/of_gpio.h> #include <linux/of_gpio.h>
#include <linux/ieee802154.h>
#include <net/ieee802154.h>
#include <net/mac802154.h> #include <net/mac802154.h>
#include <net/wpan-phy.h> #include <net/cfg802154.h>
struct at86rf230_local; struct at86rf230_local;
/* at86rf2xx chip depend data. /* at86rf2xx chip depend data.
@@ -58,7 +54,7 @@ struct at86rf2xx_chip_data {
u16 t_tx_timeout; u16 t_tx_timeout;
int rssi_base_val; int rssi_base_val;
int (*set_channel)(struct at86rf230_local *, int, int); int (*set_channel)(struct at86rf230_local *, u8, u8);
int (*get_desense_steps)(struct at86rf230_local *, s32); int (*get_desense_steps)(struct at86rf230_local *, s32);
}; };
@@ -74,12 +70,14 @@ struct at86rf230_state_change {
void (*complete)(void *context); void (*complete)(void *context);
u8 from_state; u8 from_state;
u8 to_state; u8 to_state;
bool irq_enable;
}; };
struct at86rf230_local { struct at86rf230_local {
struct spi_device *spi; struct spi_device *spi;
struct ieee802154_dev *dev; struct ieee802154_hw *hw;
struct at86rf2xx_chip_data *data; struct at86rf2xx_chip_data *data;
struct regmap *regmap; struct regmap *regmap;
@@ -89,10 +87,10 @@ struct at86rf230_local {
struct at86rf230_state_change irq; struct at86rf230_state_change irq;
bool tx_aret; bool tx_aret;
s8 max_frame_retries;
bool is_tx; bool is_tx;
/* spinlock for is_tx protection */ /* spinlock for is_tx protection */
spinlock_t lock; spinlock_t lock;
struct completion tx_complete;
struct sk_buff *tx_skb; struct sk_buff *tx_skb;
struct at86rf230_state_change tx; struct at86rf230_state_change tx;
}; };
@@ -291,10 +289,11 @@ struct at86rf230_local {
#define AT86RF2XX_NUMREGS 0x3F #define AT86RF2XX_NUMREGS 0x3F
static int static void
at86rf230_async_state_change(struct at86rf230_local *lp, at86rf230_async_state_change(struct at86rf230_local *lp,
struct at86rf230_state_change *ctx, struct at86rf230_state_change *ctx,
const u8 state, void (*complete)(void *context)); const u8 state, void (*complete)(void *context),
const bool irq_enable);
static inline int static inline int
__at86rf230_write(struct at86rf230_local *lp, __at86rf230_write(struct at86rf230_local *lp,
@@ -451,7 +450,8 @@ at86rf230_async_error_recover(void *context)
struct at86rf230_state_change *ctx = context; struct at86rf230_state_change *ctx = context;
struct at86rf230_local *lp = ctx->lp; struct at86rf230_local *lp = ctx->lp;
at86rf230_async_state_change(lp, ctx, STATE_RX_AACK_ON, NULL); at86rf230_async_state_change(lp, ctx, STATE_RX_AACK_ON, NULL, false);
ieee802154_wake_queue(lp->hw);
} }
static void static void
@@ -461,21 +461,31 @@ at86rf230_async_error(struct at86rf230_local *lp,
dev_err(&lp->spi->dev, "spi_async error %d\n", rc); dev_err(&lp->spi->dev, "spi_async error %d\n", rc);
at86rf230_async_state_change(lp, ctx, STATE_FORCE_TRX_OFF, at86rf230_async_state_change(lp, ctx, STATE_FORCE_TRX_OFF,
at86rf230_async_error_recover); at86rf230_async_error_recover, false);
} }
/* Generic function to get some register value in async mode */ /* Generic function to get some register value in async mode */
static int static void
at86rf230_async_read_reg(struct at86rf230_local *lp, const u8 reg, at86rf230_async_read_reg(struct at86rf230_local *lp, const u8 reg,
struct at86rf230_state_change *ctx, struct at86rf230_state_change *ctx,
void (*complete)(void *context)) void (*complete)(void *context),
const bool irq_enable)
{ {
int rc;
u8 *tx_buf = ctx->buf; u8 *tx_buf = ctx->buf;
tx_buf[0] = (reg & CMD_REG_MASK) | CMD_REG; tx_buf[0] = (reg & CMD_REG_MASK) | CMD_REG;
ctx->trx.len = 2; ctx->trx.len = 2;
ctx->msg.complete = complete; ctx->msg.complete = complete;
return spi_async(lp->spi, &ctx->msg); ctx->irq_enable = irq_enable;
rc = spi_async(lp->spi, &ctx->msg);
if (rc) {
if (irq_enable)
enable_irq(lp->spi->irq);
at86rf230_async_error(lp, ctx, rc);
}
} }
static void static void
@@ -512,7 +522,8 @@ at86rf230_async_state_assert(void *context)
if (ctx->to_state == STATE_TX_ON) { if (ctx->to_state == STATE_TX_ON) {
at86rf230_async_state_change(lp, ctx, at86rf230_async_state_change(lp, ctx,
STATE_FORCE_TX_ON, STATE_FORCE_TX_ON,
ctx->complete); ctx->complete,
ctx->irq_enable);
return; return;
} }
} }
@@ -535,7 +546,6 @@ at86rf230_async_state_delay(void *context)
struct at86rf230_local *lp = ctx->lp; struct at86rf230_local *lp = ctx->lp;
struct at86rf2xx_chip_data *c = lp->data; struct at86rf2xx_chip_data *c = lp->data;
bool force = false; bool force = false;
int rc;
/* The force state changes are will show as normal states in the /* The force state changes are will show as normal states in the
* state status subregister. We change the to_state to the * state status subregister. We change the to_state to the
@@ -604,10 +614,9 @@ at86rf230_async_state_delay(void *context)
udelay(1); udelay(1);
change: change:
rc = at86rf230_async_read_reg(lp, RG_TRX_STATUS, ctx, at86rf230_async_read_reg(lp, RG_TRX_STATUS, ctx,
at86rf230_async_state_assert); at86rf230_async_state_assert,
if (rc) ctx->irq_enable);
dev_err(&lp->spi->dev, "spi_async error %d\n", rc);
} }
static void static void
@@ -622,10 +631,9 @@ at86rf230_async_state_change_start(void *context)
/* Check for "possible" STATE_TRANSITION_IN_PROGRESS */ /* Check for "possible" STATE_TRANSITION_IN_PROGRESS */
if (trx_state == STATE_TRANSITION_IN_PROGRESS) { if (trx_state == STATE_TRANSITION_IN_PROGRESS) {
udelay(1); udelay(1);
rc = at86rf230_async_read_reg(lp, RG_TRX_STATUS, ctx, at86rf230_async_read_reg(lp, RG_TRX_STATUS, ctx,
at86rf230_async_state_change_start); at86rf230_async_state_change_start,
if (rc) ctx->irq_enable);
dev_err(&lp->spi->dev, "spi_async error %d\n", rc);
return; return;
} }
@@ -647,20 +655,27 @@ at86rf230_async_state_change_start(void *context)
ctx->trx.len = 2; ctx->trx.len = 2;
ctx->msg.complete = at86rf230_async_state_delay; ctx->msg.complete = at86rf230_async_state_delay;
rc = spi_async(lp->spi, &ctx->msg); rc = spi_async(lp->spi, &ctx->msg);
if (rc) if (rc) {
dev_err(&lp->spi->dev, "spi_async error %d\n", rc); if (ctx->irq_enable)
enable_irq(lp->spi->irq);
at86rf230_async_error(lp, &lp->state, rc);
}
} }
static int static void
at86rf230_async_state_change(struct at86rf230_local *lp, at86rf230_async_state_change(struct at86rf230_local *lp,
struct at86rf230_state_change *ctx, struct at86rf230_state_change *ctx,
const u8 state, void (*complete)(void *context)) const u8 state, void (*complete)(void *context),
const bool irq_enable)
{ {
/* Initialization for the state change context */ /* Initialization for the state change context */
ctx->to_state = state; ctx->to_state = state;
ctx->complete = complete; ctx->complete = complete;
return at86rf230_async_read_reg(lp, RG_TRX_STATUS, ctx, ctx->irq_enable = irq_enable;
at86rf230_async_state_change_start); at86rf230_async_read_reg(lp, RG_TRX_STATUS, ctx,
at86rf230_async_state_change_start,
irq_enable);
} }
static void static void
@@ -681,17 +696,16 @@ at86rf230_sync_state_change(struct at86rf230_local *lp, unsigned int state)
{ {
int rc; int rc;
rc = at86rf230_async_state_change(lp, &lp->state, state, at86rf230_async_state_change(lp, &lp->state, state,
at86rf230_sync_state_change_complete); at86rf230_sync_state_change_complete,
if (rc) { false);
at86rf230_async_error(lp, &lp->state, rc);
return rc;
}
rc = wait_for_completion_timeout(&lp->state_complete, rc = wait_for_completion_timeout(&lp->state_complete,
msecs_to_jiffies(100)); msecs_to_jiffies(100));
if (!rc) if (!rc) {
at86rf230_async_error(lp, &lp->state, -ETIMEDOUT);
return -ETIMEDOUT; return -ETIMEDOUT;
}
return 0; return 0;
} }
@@ -701,8 +715,23 @@ at86rf230_tx_complete(void *context)
{ {
struct at86rf230_state_change *ctx = context; struct at86rf230_state_change *ctx = context;
struct at86rf230_local *lp = ctx->lp; struct at86rf230_local *lp = ctx->lp;
struct sk_buff *skb = lp->tx_skb;
complete(&lp->tx_complete); enable_irq(lp->spi->irq);
if (lp->max_frame_retries <= 0) {
/* Interfame spacing time, which is phy depend.
* TODO
* Move this handling in MAC 802.15.4 layer.
* This is currently a workaround to avoid fragmenation issues.
*/
if (skb->len > 18)
udelay(lp->data->t_lifs);
else
udelay(lp->data->t_sifs);
}
ieee802154_xmit_complete(lp->hw, skb);
} }
static void static void
@@ -710,12 +739,9 @@ at86rf230_tx_on(void *context)
{ {
struct at86rf230_state_change *ctx = context; struct at86rf230_state_change *ctx = context;
struct at86rf230_local *lp = ctx->lp; struct at86rf230_local *lp = ctx->lp;
int rc;
rc = at86rf230_async_state_change(lp, &lp->irq, STATE_RX_AACK_ON, at86rf230_async_state_change(lp, &lp->irq, STATE_RX_AACK_ON,
at86rf230_tx_complete); at86rf230_tx_complete, true);
if (rc)
at86rf230_async_error(lp, ctx, rc);
} }
static void static void
@@ -723,12 +749,9 @@ at86rf230_tx_trac_error(void *context)
{ {
struct at86rf230_state_change *ctx = context; struct at86rf230_state_change *ctx = context;
struct at86rf230_local *lp = ctx->lp; struct at86rf230_local *lp = ctx->lp;
int rc;
rc = at86rf230_async_state_change(lp, ctx, STATE_TX_ON, at86rf230_async_state_change(lp, ctx, STATE_TX_ON,
at86rf230_tx_on); at86rf230_tx_on, true);
if (rc)
at86rf230_async_error(lp, ctx, rc);
} }
static void static void
@@ -738,17 +761,14 @@ at86rf230_tx_trac_check(void *context)
struct at86rf230_local *lp = ctx->lp; struct at86rf230_local *lp = ctx->lp;
const u8 *buf = ctx->buf; const u8 *buf = ctx->buf;
const u8 trac = (buf[1] & 0xe0) >> 5; const u8 trac = (buf[1] & 0xe0) >> 5;
int rc;
/* If trac status is different than zero we need to do a state change /* If trac status is different than zero we need to do a state change
* to STATE_FORCE_TRX_OFF then STATE_TX_ON to recover the transceiver * to STATE_FORCE_TRX_OFF then STATE_TX_ON to recover the transceiver
* state to TX_ON. * state to TX_ON.
*/ */
if (trac) { if (trac) {
rc = at86rf230_async_state_change(lp, ctx, STATE_FORCE_TRX_OFF, at86rf230_async_state_change(lp, ctx, STATE_FORCE_TRX_OFF,
at86rf230_tx_trac_error); at86rf230_tx_trac_error, true);
if (rc)
at86rf230_async_error(lp, ctx, rc);
return; return;
} }
@@ -761,51 +781,29 @@ at86rf230_tx_trac_status(void *context)
{ {
struct at86rf230_state_change *ctx = context; struct at86rf230_state_change *ctx = context;
struct at86rf230_local *lp = ctx->lp; struct at86rf230_local *lp = ctx->lp;
int rc;
rc = at86rf230_async_read_reg(lp, RG_TRX_STATE, ctx, at86rf230_async_read_reg(lp, RG_TRX_STATE, ctx,
at86rf230_tx_trac_check); at86rf230_tx_trac_check, true);
if (rc)
at86rf230_async_error(lp, ctx, rc);
} }
static void static void
at86rf230_rx(struct at86rf230_local *lp, at86rf230_rx(struct at86rf230_local *lp,
const u8 *data, u8 len) const u8 *data, const u8 len, const u8 lqi)
{ {
u8 lqi;
struct sk_buff *skb; struct sk_buff *skb;
u8 rx_local_buf[AT86RF2XX_MAX_BUF]; u8 rx_local_buf[AT86RF2XX_MAX_BUF];
if (len < 2)
return;
/* read full frame buffer and invalid lqi value to lowest
* indicator if frame was is in a corrupted state.
*/
if (len > IEEE802154_MTU) {
lqi = 0;
len = IEEE802154_MTU;
dev_vdbg(&lp->spi->dev, "corrupted frame received\n");
} else {
lqi = data[len];
}
memcpy(rx_local_buf, data, len); memcpy(rx_local_buf, data, len);
enable_irq(lp->spi->irq); enable_irq(lp->spi->irq);
skb = alloc_skb(IEEE802154_MTU, GFP_ATOMIC); skb = dev_alloc_skb(IEEE802154_MTU);
if (!skb) { if (!skb) {
dev_vdbg(&lp->spi->dev, "failed to allocate sk_buff\n"); dev_vdbg(&lp->spi->dev, "failed to allocate sk_buff\n");
return; return;
} }
memcpy(skb_put(skb, len), rx_local_buf, len); memcpy(skb_put(skb, len), rx_local_buf, len);
ieee802154_rx_irqsafe(lp->hw, skb, lqi);
/* We do not put CRC into the frame */
skb_trim(skb, len - 2);
ieee802154_rx_irqsafe(lp->dev, skb, lqi);
} }
static void static void
@@ -814,20 +812,31 @@ at86rf230_rx_read_frame_complete(void *context)
struct at86rf230_state_change *ctx = context; struct at86rf230_state_change *ctx = context;
struct at86rf230_local *lp = ctx->lp; struct at86rf230_local *lp = ctx->lp;
const u8 *buf = lp->irq.buf; const u8 *buf = lp->irq.buf;
const u8 len = buf[1]; u8 len = buf[1];
at86rf230_rx(lp, buf + 2, len); if (!ieee802154_is_valid_psdu_len(len)) {
dev_vdbg(&lp->spi->dev, "corrupted frame received\n");
len = IEEE802154_MTU;
} }
static int at86rf230_rx(lp, buf + 2, len, buf[2 + len]);
}
static void
at86rf230_rx_read_frame(struct at86rf230_local *lp) at86rf230_rx_read_frame(struct at86rf230_local *lp)
{ {
int rc;
u8 *buf = lp->irq.buf; u8 *buf = lp->irq.buf;
buf[0] = CMD_FB; buf[0] = CMD_FB;
lp->irq.trx.len = AT86RF2XX_MAX_BUF; lp->irq.trx.len = AT86RF2XX_MAX_BUF;
lp->irq.msg.complete = at86rf230_rx_read_frame_complete; lp->irq.msg.complete = at86rf230_rx_read_frame_complete;
return spi_async(lp->spi, &lp->irq.msg); rc = spi_async(lp->spi, &lp->irq.msg);
if (rc) {
enable_irq(lp->spi->irq);
at86rf230_async_error(lp, &lp->irq, rc);
}
} }
static void static void
@@ -835,7 +844,6 @@ at86rf230_rx_trac_check(void *context)
{ {
struct at86rf230_state_change *ctx = context; struct at86rf230_state_change *ctx = context;
struct at86rf230_local *lp = ctx->lp; struct at86rf230_local *lp = ctx->lp;
int rc;
/* Possible check on trac status here. This could be useful to make /* Possible check on trac status here. This could be useful to make
* some stats why receive is failed. Not used at the moment, but it's * some stats why receive is failed. Not used at the moment, but it's
@@ -843,34 +851,31 @@ at86rf230_rx_trac_check(void *context)
* The programming guide say do it so. * The programming guide say do it so.
*/ */
rc = at86rf230_rx_read_frame(lp); at86rf230_rx_read_frame(lp);
if (rc) {
enable_irq(lp->spi->irq);
at86rf230_async_error(lp, ctx, rc);
}
} }
static int static void
at86rf230_irq_trx_end(struct at86rf230_local *lp) at86rf230_irq_trx_end(struct at86rf230_local *lp)
{ {
spin_lock(&lp->lock); spin_lock(&lp->lock);
if (lp->is_tx) { if (lp->is_tx) {
lp->is_tx = 0; lp->is_tx = 0;
spin_unlock(&lp->lock); spin_unlock(&lp->lock);
enable_irq(lp->spi->irq);
if (lp->tx_aret) if (lp->tx_aret)
return at86rf230_async_state_change(lp, &lp->irq, at86rf230_async_state_change(lp, &lp->irq,
STATE_FORCE_TX_ON, STATE_FORCE_TX_ON,
at86rf230_tx_trac_status); at86rf230_tx_trac_status,
true);
else else
return at86rf230_async_state_change(lp, &lp->irq, at86rf230_async_state_change(lp, &lp->irq,
STATE_RX_AACK_ON, STATE_RX_AACK_ON,
at86rf230_tx_complete); at86rf230_tx_complete,
true);
} else { } else {
spin_unlock(&lp->lock); spin_unlock(&lp->lock);
return at86rf230_async_read_reg(lp, RG_TRX_STATE, &lp->irq, at86rf230_async_read_reg(lp, RG_TRX_STATE, &lp->irq,
at86rf230_rx_trac_check); at86rf230_rx_trac_check, true);
} }
} }
@@ -881,12 +886,9 @@ at86rf230_irq_status(void *context)
struct at86rf230_local *lp = ctx->lp; struct at86rf230_local *lp = ctx->lp;
const u8 *buf = lp->irq.buf; const u8 *buf = lp->irq.buf;
const u8 irq = buf[1]; const u8 irq = buf[1];
int rc;
if (irq & IRQ_TRX_END) { if (irq & IRQ_TRX_END) {
rc = at86rf230_irq_trx_end(lp); at86rf230_irq_trx_end(lp);
if (rc)
at86rf230_async_error(lp, ctx, rc);
} else { } else {
enable_irq(lp->spi->irq); enable_irq(lp->spi->irq);
dev_err(&lp->spi->dev, "not supported irq %02x received\n", dev_err(&lp->spi->dev, "not supported irq %02x received\n",
@@ -901,13 +903,14 @@ static irqreturn_t at86rf230_isr(int irq, void *data)
u8 *buf = ctx->buf; u8 *buf = ctx->buf;
int rc; int rc;
disable_irq_nosync(lp->spi->irq); disable_irq_nosync(irq);
buf[0] = (RG_IRQ_STATUS & CMD_REG_MASK) | CMD_REG; buf[0] = (RG_IRQ_STATUS & CMD_REG_MASK) | CMD_REG;
ctx->trx.len = 2; ctx->trx.len = 2;
ctx->msg.complete = at86rf230_irq_status; ctx->msg.complete = at86rf230_irq_status;
rc = spi_async(lp->spi, &ctx->msg); rc = spi_async(lp->spi, &ctx->msg);
if (rc) { if (rc) {
enable_irq(irq);
at86rf230_async_error(lp, ctx, rc); at86rf230_async_error(lp, ctx, rc);
return IRQ_NONE; return IRQ_NONE;
} }
@@ -960,22 +963,18 @@ at86rf230_xmit_tx_on(void *context)
{ {
struct at86rf230_state_change *ctx = context; struct at86rf230_state_change *ctx = context;
struct at86rf230_local *lp = ctx->lp; struct at86rf230_local *lp = ctx->lp;
int rc;
rc = at86rf230_async_state_change(lp, ctx, STATE_TX_ARET_ON, at86rf230_async_state_change(lp, ctx, STATE_TX_ARET_ON,
at86rf230_write_frame); at86rf230_write_frame, false);
if (rc)
at86rf230_async_error(lp, ctx, rc);
} }
static int static int
at86rf230_xmit(struct ieee802154_dev *dev, struct sk_buff *skb) at86rf230_xmit(struct ieee802154_hw *hw, struct sk_buff *skb)
{ {
struct at86rf230_local *lp = dev->priv; struct at86rf230_local *lp = hw->priv;
struct at86rf230_state_change *ctx = &lp->tx; struct at86rf230_state_change *ctx = &lp->tx;
void (*tx_complete)(void *context) = at86rf230_write_frame; void (*tx_complete)(void *context) = at86rf230_write_frame;
int rc;
lp->tx_skb = skb; lp->tx_skb = skb;
@@ -986,61 +985,39 @@ at86rf230_xmit(struct ieee802154_dev *dev, struct sk_buff *skb)
if (lp->tx_aret) if (lp->tx_aret)
tx_complete = at86rf230_xmit_tx_on; tx_complete = at86rf230_xmit_tx_on;
rc = at86rf230_async_state_change(lp, ctx, STATE_TX_ON, at86rf230_async_state_change(lp, ctx, STATE_TX_ON, tx_complete, false);
tx_complete);
if (rc) {
at86rf230_async_error(lp, ctx, rc);
return rc;
}
rc = wait_for_completion_interruptible_timeout(&lp->tx_complete,
msecs_to_jiffies(lp->data->t_tx_timeout));
if (!rc) {
at86rf230_async_error(lp, ctx, rc);
return -ETIMEDOUT;
}
/* Interfame spacing time, which is phy depend.
* TODO
* Move this handling in MAC 802.15.4 layer.
* This is currently a workaround to avoid fragmenation issues.
*/
if (skb->len > 18)
usleep_range(lp->data->t_lifs, lp->data->t_lifs + 10);
else
usleep_range(lp->data->t_sifs, lp->data->t_sifs + 10);
return 0; return 0;
} }
static int static int
at86rf230_ed(struct ieee802154_dev *dev, u8 *level) at86rf230_ed(struct ieee802154_hw *hw, u8 *level)
{ {
might_sleep();
BUG_ON(!level); BUG_ON(!level);
*level = 0xbe; *level = 0xbe;
return 0; return 0;
} }
static int static int
at86rf230_start(struct ieee802154_dev *dev) at86rf230_start(struct ieee802154_hw *hw)
{ {
return at86rf230_sync_state_change(dev->priv, STATE_RX_AACK_ON); return at86rf230_sync_state_change(hw->priv, STATE_RX_AACK_ON);
} }
static void static void
at86rf230_stop(struct ieee802154_dev *dev) at86rf230_stop(struct ieee802154_hw *hw)
{ {
at86rf230_sync_state_change(dev->priv, STATE_FORCE_TRX_OFF); at86rf230_sync_state_change(hw->priv, STATE_FORCE_TRX_OFF);
} }
static int static int
at86rf23x_set_channel(struct at86rf230_local *lp, int page, int channel) at86rf23x_set_channel(struct at86rf230_local *lp, u8 page, u8 channel)
{ {
return at86rf230_write_subreg(lp, SR_CHANNEL, channel); return at86rf230_write_subreg(lp, SR_CHANNEL, channel);
} }
static int static int
at86rf212_set_channel(struct at86rf230_local *lp, int page, int channel) at86rf212_set_channel(struct at86rf230_local *lp, u8 page, u8 channel)
{ {
int rc; int rc;
@@ -1065,15 +1042,13 @@ at86rf212_set_channel(struct at86rf230_local *lp, int page, int channel)
} }
static int static int
at86rf230_channel(struct ieee802154_dev *dev, int page, int channel) at86rf230_channel(struct ieee802154_hw *hw, u8 page, u8 channel)
{ {
struct at86rf230_local *lp = dev->priv; struct at86rf230_local *lp = hw->priv;
int rc; int rc;
might_sleep();
if (page < 0 || page > 31 || if (page < 0 || page > 31 ||
!(lp->dev->phy->channels_supported[page] & BIT(channel))) { !(lp->hw->phy->channels_supported[page] & BIT(channel))) {
WARN_ON(1); WARN_ON(1);
return -EINVAL; return -EINVAL;
} }
@@ -1085,20 +1060,20 @@ at86rf230_channel(struct ieee802154_dev *dev, int page, int channel)
/* Wait for PLL */ /* Wait for PLL */
usleep_range(lp->data->t_channel_switch, usleep_range(lp->data->t_channel_switch,
lp->data->t_channel_switch + 10); lp->data->t_channel_switch + 10);
dev->phy->current_channel = channel; hw->phy->current_channel = channel;
dev->phy->current_page = page; hw->phy->current_page = page;
return 0; return 0;
} }
static int static int
at86rf230_set_hw_addr_filt(struct ieee802154_dev *dev, at86rf230_set_hw_addr_filt(struct ieee802154_hw *hw,
struct ieee802154_hw_addr_filt *filt, struct ieee802154_hw_addr_filt *filt,
unsigned long changed) unsigned long changed)
{ {
struct at86rf230_local *lp = dev->priv; struct at86rf230_local *lp = hw->priv;
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(&lp->spi->dev, dev_vdbg(&lp->spi->dev,
@@ -1107,7 +1082,7 @@ at86rf230_set_hw_addr_filt(struct ieee802154_dev *dev,
__at86rf230_write(lp, RG_SHORT_ADDR_1, addr >> 8); __at86rf230_write(lp, RG_SHORT_ADDR_1, addr >> 8);
} }
if (changed & IEEE802515_AFILT_PANID_CHANGED) { if (changed & IEEE802154_AFILT_PANID_CHANGED) {
u16 pan = le16_to_cpu(filt->pan_id); u16 pan = le16_to_cpu(filt->pan_id);
dev_vdbg(&lp->spi->dev, dev_vdbg(&lp->spi->dev,
@@ -1116,7 +1091,7 @@ at86rf230_set_hw_addr_filt(struct ieee802154_dev *dev,
__at86rf230_write(lp, RG_PAN_ID_1, pan >> 8); __at86rf230_write(lp, RG_PAN_ID_1, pan >> 8);
} }
if (changed & IEEE802515_AFILT_IEEEADDR_CHANGED) { if (changed & IEEE802154_AFILT_IEEEADDR_CHANGED) {
u8 i, addr[8]; u8 i, addr[8];
memcpy(addr, &filt->ieee_addr, 8); memcpy(addr, &filt->ieee_addr, 8);
@@ -1126,7 +1101,7 @@ at86rf230_set_hw_addr_filt(struct ieee802154_dev *dev,
__at86rf230_write(lp, RG_IEEE_ADDR_0 + i, addr[i]); __at86rf230_write(lp, RG_IEEE_ADDR_0 + i, addr[i]);
} }
if (changed & IEEE802515_AFILT_PANC_CHANGED) { if (changed & IEEE802154_AFILT_PANC_CHANGED) {
dev_vdbg(&lp->spi->dev, dev_vdbg(&lp->spi->dev,
"at86rf230_set_hw_addr_filt called for panc change\n"); "at86rf230_set_hw_addr_filt called for panc change\n");
if (filt->pan_coord) if (filt->pan_coord)
@@ -1139,9 +1114,9 @@ at86rf230_set_hw_addr_filt(struct ieee802154_dev *dev,
} }
static int static int
at86rf230_set_txpower(struct ieee802154_dev *dev, int db) at86rf230_set_txpower(struct ieee802154_hw *hw, int db)
{ {
struct at86rf230_local *lp = dev->priv; struct at86rf230_local *lp = hw->priv;
/* typical maximum output is 5dBm with RG_PHY_TX_PWR 0x60, lower five /* typical maximum output is 5dBm with RG_PHY_TX_PWR 0x60, lower five
* bits decrease power in 1dB steps. 0x60 represents extra PA gain of * bits decrease power in 1dB steps. 0x60 represents extra PA gain of
@@ -1158,17 +1133,17 @@ at86rf230_set_txpower(struct ieee802154_dev *dev, int db)
} }
static int static int
at86rf230_set_lbt(struct ieee802154_dev *dev, bool on) at86rf230_set_lbt(struct ieee802154_hw *hw, bool on)
{ {
struct at86rf230_local *lp = dev->priv; struct at86rf230_local *lp = hw->priv;
return at86rf230_write_subreg(lp, SR_CSMA_LBT_MODE, on); return at86rf230_write_subreg(lp, SR_CSMA_LBT_MODE, on);
} }
static int static int
at86rf230_set_cca_mode(struct ieee802154_dev *dev, u8 mode) at86rf230_set_cca_mode(struct ieee802154_hw *hw, u8 mode)
{ {
struct at86rf230_local *lp = dev->priv; struct at86rf230_local *lp = hw->priv;
return at86rf230_write_subreg(lp, SR_CCA_MODE, mode); return at86rf230_write_subreg(lp, SR_CCA_MODE, mode);
} }
@@ -1186,9 +1161,9 @@ at86rf23x_get_desens_steps(struct at86rf230_local *lp, s32 level)
} }
static int static int
at86rf230_set_cca_ed_level(struct ieee802154_dev *dev, s32 level) at86rf230_set_cca_ed_level(struct ieee802154_hw *hw, s32 level)
{ {
struct at86rf230_local *lp = dev->priv; struct at86rf230_local *lp = hw->priv;
if (level < lp->data->rssi_base_val || level > 30) if (level < lp->data->rssi_base_val || level > 30)
return -EINVAL; return -EINVAL;
@@ -1198,10 +1173,10 @@ at86rf230_set_cca_ed_level(struct ieee802154_dev *dev, s32 level)
} }
static int static int
at86rf230_set_csma_params(struct ieee802154_dev *dev, u8 min_be, u8 max_be, at86rf230_set_csma_params(struct ieee802154_hw *hw, u8 min_be, u8 max_be,
u8 retries) u8 retries)
{ {
struct at86rf230_local *lp = dev->priv; struct at86rf230_local *lp = hw->priv;
int rc; int rc;
if (min_be > max_be || max_be > 8 || retries > 5) if (min_be > max_be || max_be > 8 || retries > 5)
@@ -1219,15 +1194,16 @@ at86rf230_set_csma_params(struct ieee802154_dev *dev, u8 min_be, u8 max_be,
} }
static int static int
at86rf230_set_frame_retries(struct ieee802154_dev *dev, s8 retries) at86rf230_set_frame_retries(struct ieee802154_hw *hw, s8 retries)
{ {
struct at86rf230_local *lp = dev->priv; struct at86rf230_local *lp = hw->priv;
int rc = 0; int rc = 0;
if (retries < -1 || retries > 15) if (retries < -1 || retries > 15)
return -EINVAL; return -EINVAL;
lp->tx_aret = retries >= 0; lp->tx_aret = retries >= 0;
lp->max_frame_retries = retries;
if (retries >= 0) if (retries >= 0)
rc = at86rf230_write_subreg(lp, SR_MAX_FRAME_RETRIES, retries); rc = at86rf230_write_subreg(lp, SR_MAX_FRAME_RETRIES, retries);
@@ -1235,9 +1211,36 @@ at86rf230_set_frame_retries(struct ieee802154_dev *dev, s8 retries)
return rc; return rc;
} }
static struct ieee802154_ops at86rf230_ops = { static int
at86rf230_set_promiscuous_mode(struct ieee802154_hw *hw, const bool on)
{
struct at86rf230_local *lp = hw->priv;
int rc;
if (on) {
rc = at86rf230_write_subreg(lp, SR_AACK_DIS_ACK, 1);
if (rc < 0)
return rc;
rc = at86rf230_write_subreg(lp, SR_AACK_PROM_MODE, 1);
if (rc < 0)
return rc;
} else {
rc = at86rf230_write_subreg(lp, SR_AACK_PROM_MODE, 0);
if (rc < 0)
return rc;
rc = at86rf230_write_subreg(lp, SR_AACK_DIS_ACK, 0);
if (rc < 0)
return rc;
}
return 0;
}
static const struct ieee802154_ops at86rf230_ops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.xmit = at86rf230_xmit, .xmit_async = at86rf230_xmit,
.ed = at86rf230_ed, .ed = at86rf230_ed,
.set_channel = at86rf230_channel, .set_channel = at86rf230_channel,
.start = at86rf230_start, .start = at86rf230_start,
@@ -1249,6 +1252,7 @@ static struct ieee802154_ops at86rf230_ops = {
.set_cca_ed_level = at86rf230_set_cca_ed_level, .set_cca_ed_level = at86rf230_set_cca_ed_level,
.set_csma_params = at86rf230_set_csma_params, .set_csma_params = at86rf230_set_csma_params,
.set_frame_retries = at86rf230_set_frame_retries, .set_frame_retries = at86rf230_set_frame_retries,
.set_promiscuous_mode = at86rf230_set_promiscuous_mode,
}; };
static struct at86rf2xx_chip_data at86rf233_data = { static struct at86rf2xx_chip_data at86rf233_data = {
@@ -1260,7 +1264,7 @@ static struct at86rf2xx_chip_data at86rf233_data = {
.t_frame = 4096, .t_frame = 4096,
.t_p_ack = 545, .t_p_ack = 545,
.t_sifs = 192, .t_sifs = 192,
.t_lifs = 480, .t_lifs = 640,
.t_tx_timeout = 2000, .t_tx_timeout = 2000,
.rssi_base_val = -91, .rssi_base_val = -91,
.set_channel = at86rf23x_set_channel, .set_channel = at86rf23x_set_channel,
@@ -1276,7 +1280,7 @@ static struct at86rf2xx_chip_data at86rf231_data = {
.t_frame = 4096, .t_frame = 4096,
.t_p_ack = 545, .t_p_ack = 545,
.t_sifs = 192, .t_sifs = 192,
.t_lifs = 480, .t_lifs = 640,
.t_tx_timeout = 2000, .t_tx_timeout = 2000,
.rssi_base_val = -91, .rssi_base_val = -91,
.set_channel = at86rf23x_set_channel, .set_channel = at86rf23x_set_channel,
@@ -1292,7 +1296,7 @@ static struct at86rf2xx_chip_data at86rf212_data = {
.t_frame = 4096, .t_frame = 4096,
.t_p_ack = 545, .t_p_ack = 545,
.t_sifs = 192, .t_sifs = 192,
.t_lifs = 480, .t_lifs = 640,
.t_tx_timeout = 2000, .t_tx_timeout = 2000,
.rssi_base_val = -100, .rssi_base_val = -100,
.set_channel = at86rf212_set_channel, .set_channel = at86rf212_set_channel,
@@ -1409,9 +1413,10 @@ at86rf230_detect_device(struct at86rf230_local *lp)
return -EINVAL; return -EINVAL;
} }
lp->dev->extra_tx_headroom = 0; lp->hw->extra_tx_headroom = 0;
lp->dev->flags = IEEE802154_HW_OMIT_CKSUM | IEEE802154_HW_AACK | lp->hw->flags = IEEE802154_HW_TX_OMIT_CKSUM | IEEE802154_HW_AACK |
IEEE802154_HW_TXPOWER | IEEE802154_HW_CSMA; IEEE802154_HW_TXPOWER | IEEE802154_HW_ARET |
IEEE802154_HW_AFILT | IEEE802154_HW_PROMISCUOUS;
switch (part) { switch (part) {
case 2: case 2:
@@ -1421,15 +1426,15 @@ at86rf230_detect_device(struct at86rf230_local *lp)
case 3: case 3:
chip = "at86rf231"; chip = "at86rf231";
lp->data = &at86rf231_data; lp->data = &at86rf231_data;
lp->dev->phy->channels_supported[0] = 0x7FFF800; lp->hw->phy->channels_supported[0] = 0x7FFF800;
break; break;
case 7: case 7:
chip = "at86rf212"; chip = "at86rf212";
if (version == 1) { if (version == 1) {
lp->data = &at86rf212_data; lp->data = &at86rf212_data;
lp->dev->flags |= IEEE802154_HW_LBT; lp->hw->flags |= IEEE802154_HW_LBT;
lp->dev->phy->channels_supported[0] = 0x00007FF; lp->hw->phy->channels_supported[0] = 0x00007FF;
lp->dev->phy->channels_supported[2] = 0x00007FF; lp->hw->phy->channels_supported[2] = 0x00007FF;
} else { } else {
rc = -ENOTSUPP; rc = -ENOTSUPP;
} }
@@ -1437,7 +1442,7 @@ at86rf230_detect_device(struct at86rf230_local *lp)
case 11: case 11:
chip = "at86rf233"; chip = "at86rf233";
lp->data = &at86rf233_data; lp->data = &at86rf233_data;
lp->dev->phy->channels_supported[0] = 0x7FFF800; lp->hw->phy->channels_supported[0] = 0x7FFF800;
break; break;
default: default:
chip = "unkown"; chip = "unkown";
@@ -1478,7 +1483,7 @@ at86rf230_setup_spi_messages(struct at86rf230_local *lp)
static int at86rf230_probe(struct spi_device *spi) static int at86rf230_probe(struct spi_device *spi)
{ {
struct at86rf230_platform_data *pdata; struct at86rf230_platform_data *pdata;
struct ieee802154_dev *dev; struct ieee802154_hw *hw;
struct at86rf230_local *lp; struct at86rf230_local *lp;
unsigned int status; unsigned int status;
int rc, irq_type; int rc, irq_type;
@@ -1517,14 +1522,14 @@ static int at86rf230_probe(struct spi_device *spi)
usleep_range(120, 240); usleep_range(120, 240);
} }
dev = ieee802154_alloc_device(sizeof(*lp), &at86rf230_ops); hw = ieee802154_alloc_hw(sizeof(*lp), &at86rf230_ops);
if (!dev) if (!hw)
return -ENOMEM; return -ENOMEM;
lp = dev->priv; lp = hw->priv;
lp->dev = dev; lp->hw = hw;
lp->spi = spi; lp->spi = spi;
dev->parent = &spi->dev; hw->parent = &spi->dev;
lp->regmap = devm_regmap_init_spi(spi, &at86rf230_regmap_spi_config); lp->regmap = devm_regmap_init_spi(spi, &at86rf230_regmap_spi_config);
if (IS_ERR(lp->regmap)) { if (IS_ERR(lp->regmap)) {
@@ -1541,7 +1546,6 @@ static int at86rf230_probe(struct spi_device *spi)
goto free_dev; goto free_dev;
spin_lock_init(&lp->lock); spin_lock_init(&lp->lock);
init_completion(&lp->tx_complete);
init_completion(&lp->state_complete); init_completion(&lp->state_complete);
spi_set_drvdata(spi, lp); spi_set_drvdata(spi, lp);
@@ -1564,14 +1568,14 @@ static int at86rf230_probe(struct spi_device *spi)
if (rc) if (rc)
goto free_dev; goto free_dev;
rc = ieee802154_register_device(lp->dev); rc = ieee802154_register_hw(lp->hw);
if (rc) if (rc)
goto free_dev; goto free_dev;
return rc; return rc;
free_dev: free_dev:
ieee802154_free_device(lp->dev); ieee802154_free_hw(lp->hw);
return rc; return rc;
} }
@@ -1582,8 +1586,8 @@ static int at86rf230_remove(struct spi_device *spi)
/* mask all at86rf230 irq's */ /* mask all at86rf230 irq's */
at86rf230_write_subreg(lp, SR_IRQ_MASK, 0); at86rf230_write_subreg(lp, SR_IRQ_MASK, 0);
ieee802154_unregister_device(lp->dev); ieee802154_unregister_hw(lp->hw);
ieee802154_free_device(lp->dev); ieee802154_free_hw(lp->hw);
dev_dbg(&spi->dev, "unregistered at86rf230\n"); dev_dbg(&spi->dev, "unregistered at86rf230\n");
return 0; return 0;

View File

@@ -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 @@
/* 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)
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)
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)
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)
} }
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)
} }
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)
} }
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,
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,
(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,
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,
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)
{ {
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)
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;
} }

View File

@@ -1,427 +0,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");

View File

@@ -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 @@
#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 {
}; };
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)
} }
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)
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)
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)
} }
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) {
} }
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)
{ {
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)
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)
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)

View File

@@ -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 @@
#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 @@
#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 @@
#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 @@ out:
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 @@ err:
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)
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)
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)
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,
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,
"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,
#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,
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)
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)
/* 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 @@ out:
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)
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)
/* 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)
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)
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)
} }
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);

View File

@@ -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 @@
* 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 {
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 */

View File

@@ -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

View File

@@ -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);

View File

@@ -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>

View File

@@ -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 {
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);
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);

View File

@@ -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 {
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)
{ {
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)
{ {
return dev_name(&phy->dev); return dev_name(&phy->dev);
} }
#endif
#endif /* __NET_CFG802154_H */

View File

@@ -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 @@
#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)

View File

@@ -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 @@
*/ */
/* 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 {
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,8 +73,8 @@ 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. */
@@ -93,11 +90,26 @@ struct ieee802154_dev {
#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 {
* 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 {
* 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);
int (*set_hw_addr_filt)(struct ieee802154_hw *hw,
struct ieee802154_hw_addr_filt *filt, 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 */

View File

@@ -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

View File

@@ -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 @@ err:
/* 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,
/* 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,
/* 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,
(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,
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);
skb_push(skb, sizeof(hdr));
skb_reset_network_header(skb);
skb_copy_to_linear_data(skb, &hdr, sizeof(hdr));
raw_dump_table(__func__, "raw header dump", (u8 *)&hdr, sizeof(hdr)); raw_dump_table(__func__, "raw header dump", (u8 *)&hdr, sizeof(hdr));
return skb_deliver(skb, &hdr, dev, deliver_skb); 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,

View File

@@ -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;
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 {
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)
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)
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,
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,
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,
* 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();
peer = list_first_or_null_rcu(&dev->peers, struct lowpan_peer,
list); 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,
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)
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 netif_rx(skb_cp);
} }
return ret; static int iphc_decompress(struct sk_buff *skb, struct net_device *netdev,
}
static int process_data(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,
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,
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,
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,
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,
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,
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,
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)
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)
} }
} }
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)
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,
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,
*/ */
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)
{ {
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)
(*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)
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)
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)
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)
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,
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)
* 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)
} }
} }
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;
};
psm = val; static void do_psm_set(struct work_struct *work)
if (psm == 0 || psm_6lowpan != psm) {
struct set_psm *set_psm = container_of(work, struct set_psm, work);
if (set_psm->psm == 0 || psm_6lowpan != set_psm->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)
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,
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)
{ {
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)
* 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)
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,
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,
break; break;
} }
} }
write_unlock_irqrestore(&devices_lock, flags); spin_unlock(&devices_lock);
break; break;
} }

View File

@@ -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)
* 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)
/* 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)
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);

View File

@@ -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,
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,
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,
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,
__skb_queue_tail(queue, skb); __skb_queue_tail(queue, skb);
} while (list); } while (list);
spin_unlock(&queue->lock); spin_unlock_bh(&queue->lock);
} }
} }

View File

@@ -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)
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,
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,
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,
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)
} }
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,7 +4265,8 @@ 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,
bdaddr_t *addr,
u8 addr_type, u8 adv_type) u8 addr_type, u8 adv_type)
{ {
struct hci_conn *conn; struct hci_conn *conn;
@@ -4277,17 +4274,17 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr,
/* 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,
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,
* 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,
*/ */
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,
* 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,
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,
{ {
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,
} }
/* 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

View File

@@ -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;

View File

@@ -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,
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,
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.

View File

@@ -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,
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,
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)
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,
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);
/* We must ensure that the EIR Data fields are ordered and
* unique. Keep it simple for now and avoid the problem by not
* 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) if (name_len > 0)
eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE, eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE,
name, name_len); 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);

View File

@@ -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)
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 */

View File

@@ -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],
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],
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)
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)
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)
__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)
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)
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)
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;

View File

@@ -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 @@
#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,
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)
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,
&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,
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,
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,
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,
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,
/* 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,
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)
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 = {
.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)
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)
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)
{ {
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);

View File

@@ -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__

View File

@@ -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>

View File

@@ -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>

View File

@@ -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 @@
#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)
{ {
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)
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);
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);

View File

@@ -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 @@
#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>

View File

@@ -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

View File

@@ -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

View File

@@ -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>

View File

@@ -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 @@
#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 @@
#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)
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)
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;

View File

@@ -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 @@
#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 */

View File

@@ -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>

View File

@@ -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>

View File

@@ -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;
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,
{ {
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);

View File

@@ -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 {
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;

94
net/ieee802154/sysfs.c Normal file
View File

@@ -0,0 +1,94 @@
/* 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);
}

9
net/ieee802154/sysfs.h Normal file
View File

@@ -0,0 +1,9 @@
#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 */

View File

@@ -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.

View File

@@ -1,5 +1,5 @@
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__

226
net/mac802154/driver-ops.h Normal file
View File

@@ -0,0 +1,226 @@
#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 */

View File

@@ -1,415 +0,0 @@
/*
* Copyright (C) 2007-2012 Siemens AG
*
* Written by:
* Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
*
* Based on the code from 'linux-zigbee.sourceforge.net' project.
*
* 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.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <net/netlink.h>
#include <linux/nl802154.h>
#include <net/mac802154.h>
#include <net/ieee802154_netdev.h>
#include <net/route.h>
#include <net/wpan-phy.h>
#include "mac802154.h"
int mac802154_slave_open(struct net_device *dev)
{
struct mac802154_sub_if_data *priv = netdev_priv(dev);
struct mac802154_sub_if_data *subif;
struct mac802154_priv *ipriv = priv->hw;
int res = 0;
ASSERT_RTNL();
if (priv->type == IEEE802154_DEV_WPAN) {
mutex_lock(&priv->hw->slaves_mtx);
list_for_each_entry(subif, &priv->hw->slaves, list) {
if (subif != priv && subif->type == priv->type &&
subif->running) {
mutex_unlock(&priv->hw->slaves_mtx);
return -EBUSY;
}
}
mutex_unlock(&priv->hw->slaves_mtx);
}
mutex_lock(&priv->hw->slaves_mtx);
priv->running = true;
mutex_unlock(&priv->hw->slaves_mtx);
if (ipriv->open_count++ == 0) {
res = ipriv->ops->start(&ipriv->hw);
WARN_ON(res);
if (res)
goto err;
}
if (ipriv->ops->ieee_addr) {
__le64 addr = ieee802154_devaddr_from_raw(dev->dev_addr);
res = ipriv->ops->ieee_addr(&ipriv->hw, addr);
WARN_ON(res);
if (res)
goto err;
mac802154_dev_set_ieee_addr(dev);
}
netif_start_queue(dev);
return 0;
err:
priv->hw->open_count--;
return res;
}
int mac802154_slave_close(struct net_device *dev)
{
struct mac802154_sub_if_data *priv = netdev_priv(dev);
struct mac802154_priv *ipriv = priv->hw;
ASSERT_RTNL();
netif_stop_queue(dev);
mutex_lock(&priv->hw->slaves_mtx);
priv->running = false;
mutex_unlock(&priv->hw->slaves_mtx);
if (!--ipriv->open_count)
ipriv->ops->stop(&ipriv->hw);
return 0;
}
static int
mac802154_netdev_register(struct wpan_phy *phy, struct net_device *dev)
{
struct mac802154_sub_if_data *priv;
struct mac802154_priv *ipriv;
int err;
ipriv = wpan_phy_priv(phy);
priv = netdev_priv(dev);
priv->dev = dev;
priv->hw = ipriv;
dev->needed_headroom = ipriv->hw.extra_tx_headroom;
SET_NETDEV_DEV(dev, &ipriv->phy->dev);
mutex_lock(&ipriv->slaves_mtx);
if (!ipriv->running) {
mutex_unlock(&ipriv->slaves_mtx);
return -ENODEV;
}
mutex_unlock(&ipriv->slaves_mtx);
err = register_netdev(dev);
if (err < 0)
return err;
rtnl_lock();
mutex_lock(&ipriv->slaves_mtx);
list_add_tail_rcu(&priv->list, &ipriv->slaves);
mutex_unlock(&ipriv->slaves_mtx);
rtnl_unlock();
return 0;
}
static void
mac802154_del_iface(struct wpan_phy *phy, struct net_device *dev)
{
struct mac802154_sub_if_data *sdata;
ASSERT_RTNL();
sdata = netdev_priv(dev);
BUG_ON(sdata->hw->phy != phy);
mutex_lock(&sdata->hw->slaves_mtx);
list_del_rcu(&sdata->list);
mutex_unlock(&sdata->hw->slaves_mtx);
synchronize_rcu();
unregister_netdevice(sdata->dev);
}
static struct net_device *
mac802154_add_iface(struct wpan_phy *phy, const char *name, int type)
{
struct net_device *dev;
int err = -ENOMEM;
switch (type) {
case IEEE802154_DEV_MONITOR:
dev = alloc_netdev(sizeof(struct mac802154_sub_if_data),
name, NET_NAME_UNKNOWN,
mac802154_monitor_setup);
break;
case IEEE802154_DEV_WPAN:
dev = alloc_netdev(sizeof(struct mac802154_sub_if_data),
name, NET_NAME_UNKNOWN,
mac802154_wpan_setup);
break;
default:
dev = NULL;
err = -EINVAL;
break;
}
if (!dev)
goto err;
err = mac802154_netdev_register(phy, dev);
if (err)
goto err_free;
dev_hold(dev); /* we return an incremented device refcount */
return dev;
err_free:
free_netdev(dev);
err:
return ERR_PTR(err);
}
static int mac802154_set_txpower(struct wpan_phy *phy, int db)
{
struct mac802154_priv *priv = wpan_phy_priv(phy);
return priv->ops->set_txpower(&priv->hw, db);
}
static int mac802154_set_lbt(struct wpan_phy *phy, bool on)
{
struct mac802154_priv *priv = wpan_phy_priv(phy);
return priv->ops->set_lbt(&priv->hw, on);
}
static int mac802154_set_cca_mode(struct wpan_phy *phy, u8 mode)
{
struct mac802154_priv *priv = wpan_phy_priv(phy);
return priv->ops->set_cca_mode(&priv->hw, mode);
}
static int mac802154_set_cca_ed_level(struct wpan_phy *phy, s32 level)
{
struct mac802154_priv *priv = wpan_phy_priv(phy);
return priv->ops->set_cca_ed_level(&priv->hw, level);
}
static int mac802154_set_csma_params(struct wpan_phy *phy, u8 min_be,
u8 max_be, u8 retries)
{
struct mac802154_priv *priv = wpan_phy_priv(phy);
return priv->ops->set_csma_params(&priv->hw, min_be, max_be, retries);
}
static int mac802154_set_frame_retries(struct wpan_phy *phy, s8 retries)
{
struct mac802154_priv *priv = wpan_phy_priv(phy);
return priv->ops->set_frame_retries(&priv->hw, retries);
}
struct ieee802154_dev *
ieee802154_alloc_device(size_t priv_data_len, struct ieee802154_ops *ops)
{
struct wpan_phy *phy;
struct mac802154_priv *priv;
size_t priv_size;
if (!ops || !ops->xmit || !ops->ed || !ops->start ||
!ops->stop || !ops->set_channel) {
pr_err("undefined IEEE802.15.4 device operations\n");
return NULL;
}
/* Ensure 32-byte alignment of our private data and hw private data.
* We use the wpan_phy priv data for both our mac802154_priv and for
* the driver's private data
*
* in memory it'll be like this:
*
* +-----------------------+
* | struct wpan_phy |
* +-----------------------+
* | struct mac802154_priv |
* +-----------------------+
* | driver's private data |
* +-----------------------+
*
* Due to ieee802154 layer isn't aware of driver and MAC structures,
* so lets allign them here.
*/
priv_size = ALIGN(sizeof(*priv), NETDEV_ALIGN) + priv_data_len;
phy = wpan_phy_alloc(priv_size);
if (!phy) {
pr_err("failure to allocate master IEEE802.15.4 device\n");
return NULL;
}
priv = wpan_phy_priv(phy);
priv->phy = phy;
priv->hw.phy = priv->phy;
priv->hw.priv = (char *)priv + ALIGN(sizeof(*priv), NETDEV_ALIGN);
priv->ops = ops;
INIT_LIST_HEAD(&priv->slaves);
mutex_init(&priv->slaves_mtx);
return &priv->hw;
}
EXPORT_SYMBOL(ieee802154_alloc_device);
void ieee802154_free_device(struct ieee802154_dev *hw)
{
struct mac802154_priv *priv = mac802154_to_priv(hw);
BUG_ON(!list_empty(&priv->slaves));
mutex_destroy(&priv->slaves_mtx);
wpan_phy_free(priv->phy);
}
EXPORT_SYMBOL(ieee802154_free_device);
int ieee802154_register_device(struct ieee802154_dev *dev)
{
struct mac802154_priv *priv = mac802154_to_priv(dev);
int rc = -ENOSYS;
if (dev->flags & IEEE802154_HW_TXPOWER) {
if (!priv->ops->set_txpower)
goto out;
priv->phy->set_txpower = mac802154_set_txpower;
}
if (dev->flags & IEEE802154_HW_LBT) {
if (!priv->ops->set_lbt)
goto out;
priv->phy->set_lbt = mac802154_set_lbt;
}
if (dev->flags & IEEE802154_HW_CCA_MODE) {
if (!priv->ops->set_cca_mode)
goto out;
priv->phy->set_cca_mode = mac802154_set_cca_mode;
}
if (dev->flags & IEEE802154_HW_CCA_ED_LEVEL) {
if (!priv->ops->set_cca_ed_level)
goto out;
priv->phy->set_cca_ed_level = mac802154_set_cca_ed_level;
}
if (dev->flags & IEEE802154_HW_CSMA_PARAMS) {
if (!priv->ops->set_csma_params)
goto out;
priv->phy->set_csma_params = mac802154_set_csma_params;
}
if (dev->flags & IEEE802154_HW_FRAME_RETRIES) {
if (!priv->ops->set_frame_retries)
goto out;
priv->phy->set_frame_retries = mac802154_set_frame_retries;
}
priv->dev_workqueue =
create_singlethread_workqueue(wpan_phy_name(priv->phy));
if (!priv->dev_workqueue) {
rc = -ENOMEM;
goto out;
}
wpan_phy_set_dev(priv->phy, priv->hw.parent);
priv->phy->add_iface = mac802154_add_iface;
priv->phy->del_iface = mac802154_del_iface;
rc = wpan_phy_register(priv->phy);
if (rc < 0)
goto out_wq;
rtnl_lock();
mutex_lock(&priv->slaves_mtx);
priv->running = MAC802154_DEVICE_RUN;
mutex_unlock(&priv->slaves_mtx);
rtnl_unlock();
return 0;
out_wq:
destroy_workqueue(priv->dev_workqueue);
out:
return rc;
}
EXPORT_SYMBOL(ieee802154_register_device);
void ieee802154_unregister_device(struct ieee802154_dev *dev)
{
struct mac802154_priv *priv = mac802154_to_priv(dev);
struct mac802154_sub_if_data *sdata, *next;
flush_workqueue(priv->dev_workqueue);
destroy_workqueue(priv->dev_workqueue);
rtnl_lock();
mutex_lock(&priv->slaves_mtx);
priv->running = MAC802154_DEVICE_STOPPED;
mutex_unlock(&priv->slaves_mtx);
list_for_each_entry_safe(sdata, next, &priv->slaves, list) {
mutex_lock(&sdata->hw->slaves_mtx);
list_del(&sdata->list);
mutex_unlock(&sdata->hw->slaves_mtx);
unregister_netdevice(sdata->dev);
}
rtnl_unlock();
wpan_phy_unregister(priv->phy);
}
EXPORT_SYMBOL(ieee802154_unregister_device);
MODULE_DESCRIPTION("IEEE 802.15.4 implementation");
MODULE_LICENSE("GPL v2");

View File

@@ -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 @@
#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 {
* *
* 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 {
* 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 {
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);
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,
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 */

466
net/mac802154/iface.c Normal file
View File

@@ -0,0 +1,466 @@
/*
* Copyright 2007-2012 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.
*
* 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/module.h>
#include <linux/if_arp.h>
#include <linux/ieee802154.h>
#include <net/rtnetlink.h>
#include <linux/nl802154.h>
#include <net/af_ieee802154.h>
#include <net/mac802154.h>
#include <net/ieee802154_netdev.h>
#include <net/cfg802154.h>
#include "ieee802154_i.h"
#include "driver-ops.h"
static int mac802154_wpan_update_llsec(struct net_device *dev)
{
struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
int rc = 0;
if (ops->llsec) {
struct ieee802154_llsec_params params;
int changed = 0;
params.pan_id = sdata->pan_id;
changed |= IEEE802154_LLSEC_PARAM_PAN_ID;
params.hwaddr = sdata->extended_addr;
changed |= IEEE802154_LLSEC_PARAM_HWADDR;
rc = ops->llsec->set_params(dev, &params, changed);
}
return rc;
}
static int
mac802154_wpan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
struct sockaddr_ieee802154 *sa =
(struct sockaddr_ieee802154 *)&ifr->ifr_addr;
int err = -ENOIOCTLCMD;
spin_lock_bh(&sdata->mib_lock);
switch (cmd) {
case SIOCGIFADDR:
{
u16 pan_id, short_addr;
pan_id = le16_to_cpu(sdata->pan_id);
short_addr = le16_to_cpu(sdata->short_addr);
if (pan_id == IEEE802154_PANID_BROADCAST ||
short_addr == IEEE802154_ADDR_BROADCAST) {
err = -EADDRNOTAVAIL;
break;
}
sa->family = AF_IEEE802154;
sa->addr.addr_type = IEEE802154_ADDR_SHORT;
sa->addr.pan_id = pan_id;
sa->addr.short_addr = short_addr;
err = 0;
break;
}
case SIOCSIFADDR:
dev_warn(&dev->dev,
"Using DEBUGing ioctl SIOCSIFADDR isn't recommended!\n");
if (sa->family != AF_IEEE802154 ||
sa->addr.addr_type != IEEE802154_ADDR_SHORT ||
sa->addr.pan_id == IEEE802154_PANID_BROADCAST ||
sa->addr.short_addr == IEEE802154_ADDR_BROADCAST ||
sa->addr.short_addr == IEEE802154_ADDR_UNDEF) {
err = -EINVAL;
break;
}
sdata->pan_id = cpu_to_le16(sa->addr.pan_id);
sdata->short_addr = cpu_to_le16(sa->addr.short_addr);
err = mac802154_wpan_update_llsec(dev);
break;
}
spin_unlock_bh(&sdata->mib_lock);
return err;
}
static int mac802154_wpan_mac_addr(struct net_device *dev, void *p)
{
struct sockaddr *addr = p;
if (netif_running(dev))
return -EBUSY;
/* FIXME: validate addr */
memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
mac802154_dev_set_ieee_addr(dev);
return mac802154_wpan_update_llsec(dev);
}
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;
}
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 int mac802154_slave_open(struct net_device *dev)
{
struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
struct ieee802154_sub_if_data *subif;
struct ieee802154_local *local = sdata->local;
int res = 0;
ASSERT_RTNL();
if (sdata->type == IEEE802154_DEV_WPAN) {
mutex_lock(&sdata->local->iflist_mtx);
list_for_each_entry(subif, &sdata->local->interfaces, list) {
if (subif != sdata && subif->type == sdata->type &&
ieee802154_sdata_running(subif)) {
mutex_unlock(&sdata->local->iflist_mtx);
return -EBUSY;
}
}
mutex_unlock(&sdata->local->iflist_mtx);
}
set_bit(SDATA_STATE_RUNNING, &sdata->state);
if (!local->open_count) {
res = drv_start(local);
WARN_ON(res);
if (res)
goto err;
}
local->open_count++;
netif_start_queue(dev);
return 0;
err:
/* might already be clear but that doesn't matter */
clear_bit(SDATA_STATE_RUNNING, &sdata->state);
return res;
}
static int mac802154_wpan_open(struct net_device *dev)
{
int rc;
struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
struct ieee802154_local *local = sdata->local;
struct wpan_phy *phy = sdata->local->phy;
rc = mac802154_slave_open(dev);
if (rc < 0)
return rc;
mutex_lock(&phy->pib_lock);
if (local->hw.flags & IEEE802154_HW_PROMISCUOUS) {
rc = drv_set_promiscuous_mode(local, sdata->promisuous_mode);
if (rc < 0)
goto out;
}
if (local->hw.flags & IEEE802154_HW_TXPOWER) {
rc = drv_set_tx_power(local, sdata->mac_params.transmit_power);
if (rc < 0)
goto out;
}
if (local->hw.flags & IEEE802154_HW_LBT) {
rc = drv_set_lbt_mode(local, sdata->mac_params.lbt);
if (rc < 0)
goto out;
}
if (local->hw.flags & IEEE802154_HW_CCA_MODE) {
rc = drv_set_cca_mode(local, sdata->mac_params.cca_mode);
if (rc < 0)
goto out;
}
if (local->hw.flags & IEEE802154_HW_CCA_ED_LEVEL) {
rc = drv_set_cca_ed_level(local,
sdata->mac_params.cca_ed_level);
if (rc < 0)
goto out;
}
if (local->hw.flags & IEEE802154_HW_CSMA_PARAMS) {
rc = drv_set_csma_params(local, sdata->mac_params.min_be,
sdata->mac_params.max_be,
sdata->mac_params.csma_retries);
if (rc < 0)
goto out;
}
if (local->hw.flags & IEEE802154_HW_FRAME_RETRIES) {
rc = drv_set_max_frame_retries(local,
sdata->mac_params.frame_retries);
if (rc < 0)
goto out;
}
mutex_unlock(&phy->pib_lock);
return 0;
out:
mutex_unlock(&phy->pib_lock);
return rc;
}
static int mac802154_slave_close(struct net_device *dev)
{
struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
struct ieee802154_local *local = sdata->local;
ASSERT_RTNL();
netif_stop_queue(dev);
local->open_count--;
clear_bit(SDATA_STATE_RUNNING, &sdata->state);
if (!local->open_count)
drv_stop(local);
return 0;
}
static int mac802154_set_header_security(struct ieee802154_sub_if_data *sdata,
struct ieee802154_hdr *hdr,
const struct ieee802154_mac_cb *cb)
{
struct ieee802154_llsec_params params;
u8 level;
mac802154_llsec_get_params(&sdata->sec, &params);
if (!params.enabled && cb->secen_override && cb->secen)
return -EINVAL;
if (!params.enabled ||
(cb->secen_override && !cb->secen) ||
!params.out_level)
return 0;
if (cb->seclevel_override && !cb->seclevel)
return -EINVAL;
level = cb->seclevel_override ? cb->seclevel : params.out_level;
hdr->fc.security_enabled = 1;
hdr->sec.level = level;
hdr->sec.key_id_mode = params.out_key.mode;
if (params.out_key.mode == IEEE802154_SCF_KEY_SHORT_INDEX)
hdr->sec.short_src = params.out_key.short_source;
else if (params.out_key.mode == IEEE802154_SCF_KEY_HW_INDEX)
hdr->sec.extended_src = params.out_key.extended_source;
hdr->sec.key_id = params.out_key.id;
return 0;
}
static int mac802154_header_create(struct sk_buff *skb,
struct net_device *dev,
unsigned short type,
const void *daddr,
const void *saddr,
unsigned len)
{
struct ieee802154_hdr hdr;
struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
struct ieee802154_mac_cb *cb = mac_cb(skb);
int hlen;
if (!daddr)
return -EINVAL;
memset(&hdr.fc, 0, sizeof(hdr.fc));
hdr.fc.type = cb->type;
hdr.fc.security_enabled = cb->secen;
hdr.fc.ack_request = cb->ackreq;
hdr.seq = ieee802154_mlme_ops(dev)->get_dsn(dev);
if (mac802154_set_header_security(sdata, &hdr, cb) < 0)
return -EINVAL;
if (!saddr) {
spin_lock_bh(&sdata->mib_lock);
if (sdata->short_addr == cpu_to_le16(IEEE802154_ADDR_BROADCAST) ||
sdata->short_addr == cpu_to_le16(IEEE802154_ADDR_UNDEF) ||
sdata->pan_id == cpu_to_le16(IEEE802154_PANID_BROADCAST)) {
hdr.source.mode = IEEE802154_ADDR_LONG;
hdr.source.extended_addr = sdata->extended_addr;
} else {
hdr.source.mode = IEEE802154_ADDR_SHORT;
hdr.source.short_addr = sdata->short_addr;
}
hdr.source.pan_id = sdata->pan_id;
spin_unlock_bh(&sdata->mib_lock);
} else {
hdr.source = *(const struct ieee802154_addr *)saddr;
}
hdr.dest = *(const struct ieee802154_addr *)daddr;
hlen = ieee802154_hdr_push(skb, &hdr);
if (hlen < 0)
return -EINVAL;
skb_reset_mac_header(skb);
skb->mac_len = hlen;
if (len > ieee802154_max_payload(&hdr))
return -EMSGSIZE;
return hlen;
}
static int
mac802154_header_parse(const struct sk_buff *skb, unsigned char *haddr)
{
struct ieee802154_hdr hdr;
struct ieee802154_addr *addr = (struct ieee802154_addr *)haddr;
if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0) {
pr_debug("malformed packet\n");
return 0;
}
*addr = hdr.source;
return sizeof(*addr);
}
static struct header_ops mac802154_header_ops = {
.create = mac802154_header_create,
.parse = mac802154_header_parse,
};
static const struct net_device_ops mac802154_wpan_ops = {
.ndo_open = mac802154_wpan_open,
.ndo_stop = mac802154_slave_close,
.ndo_start_xmit = ieee802154_subif_start_xmit,
.ndo_do_ioctl = mac802154_wpan_ioctl,
.ndo_set_mac_address = mac802154_wpan_mac_addr,
};
static const struct net_device_ops mac802154_monitor_ops = {
.ndo_open = mac802154_wpan_open,
.ndo_stop = mac802154_slave_close,
.ndo_start_xmit = ieee802154_monitor_start_xmit,
};
static void mac802154_wpan_free(struct net_device *dev)
{
struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
mac802154_llsec_destroy(&sdata->sec);
free_netdev(dev);
}
void mac802154_wpan_setup(struct net_device *dev)
{
struct ieee802154_sub_if_data *sdata;
dev->addr_len = IEEE802154_ADDR_LEN;
memset(dev->broadcast, 0xff, IEEE802154_ADDR_LEN);
dev->hard_header_len = MAC802154_FRAME_HARD_HEADER_LEN;
dev->header_ops = &mac802154_header_ops;
dev->needed_tailroom = 2 + 16; /* FCS + MIC */
dev->mtu = IEEE802154_MTU;
dev->tx_queue_len = 300;
dev->type = ARPHRD_IEEE802154;
dev->flags = IFF_NOARP | IFF_BROADCAST;
dev->destructor = mac802154_wpan_free;
dev->netdev_ops = &mac802154_wpan_ops;
dev->ml_priv = &mac802154_mlme_wpan;
sdata = IEEE802154_DEV_TO_SUB_IF(dev);
sdata->type = IEEE802154_DEV_WPAN;
spin_lock_init(&sdata->mib_lock);
mutex_init(&sdata->sec_mtx);
get_random_bytes(&sdata->bsn, 1);
get_random_bytes(&sdata->dsn, 1);
/* defaults per 802.15.4-2011 */
sdata->mac_params.min_be = 3;
sdata->mac_params.max_be = 5;
sdata->mac_params.csma_retries = 4;
/* for compatibility, actual default is 3 */
sdata->mac_params.frame_retries = -1;
sdata->pan_id = cpu_to_le16(IEEE802154_PANID_BROADCAST);
sdata->short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST);
sdata->promisuous_mode = false;
mac802154_llsec_init(&sdata->sec);
}
void mac802154_monitor_setup(struct net_device *dev)
{
struct ieee802154_sub_if_data *sdata;
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->destructor = free_netdev;
dev->netdev_ops = &mac802154_monitor_ops;
dev->ml_priv = &mac802154_mlme_reduced;
sdata = IEEE802154_DEV_TO_SUB_IF(dev);
sdata->type = IEEE802154_DEV_MONITOR;
sdata->promisuous_mode = true;
}

View File

@@ -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);

View File

@@ -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 @@
#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,
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 = {

265
net/mac802154/main.c Normal file
View File

@@ -0,0 +1,265 @@
/*
* Copyright (C) 2007-2012 Siemens AG
*
* Written by:
* Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
*
* Based on the code from 'linux-zigbee.sourceforge.net' project.
*
* 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.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <net/netlink.h>
#include <linux/nl802154.h>
#include <net/mac802154.h>
#include <net/ieee802154_netdev.h>
#include <net/route.h>
#include <net/cfg802154.h>
#include "ieee802154_i.h"
static int
mac802154_netdev_register(struct wpan_phy *phy, struct net_device *dev)
{
struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
struct ieee802154_local *local;
int err;
local = wpan_phy_priv(phy);
sdata->dev = dev;
sdata->local = local;
dev->needed_headroom = local->hw.extra_tx_headroom;
SET_NETDEV_DEV(dev, &local->phy->dev);
err = register_netdev(dev);
if (err < 0)
return err;
rtnl_lock();
mutex_lock(&local->iflist_mtx);
list_add_tail_rcu(&sdata->list, &local->interfaces);
mutex_unlock(&local->iflist_mtx);
rtnl_unlock();
return 0;
}
static void
mac802154_del_iface(struct wpan_phy *phy, struct net_device *dev)
{
struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
ASSERT_RTNL();
BUG_ON(sdata->local->phy != phy);
mutex_lock(&sdata->local->iflist_mtx);
list_del_rcu(&sdata->list);
mutex_unlock(&sdata->local->iflist_mtx);
synchronize_rcu();
unregister_netdevice(sdata->dev);
}
static struct net_device *
mac802154_add_iface(struct wpan_phy *phy, const char *name, int type)
{
struct net_device *dev;
int err = -ENOMEM;
switch (type) {
case IEEE802154_DEV_MONITOR:
dev = alloc_netdev(sizeof(struct ieee802154_sub_if_data),
name, NET_NAME_UNKNOWN,
mac802154_monitor_setup);
break;
case IEEE802154_DEV_WPAN:
dev = alloc_netdev(sizeof(struct ieee802154_sub_if_data),
name, NET_NAME_UNKNOWN,
mac802154_wpan_setup);
break;
default:
dev = NULL;
err = -EINVAL;
break;
}
if (!dev)
goto err;
err = mac802154_netdev_register(phy, dev);
if (err)
goto err_free;
dev_hold(dev); /* we return an incremented device refcount */
return dev;
err_free:
free_netdev(dev);
err:
return ERR_PTR(err);
}
static void ieee802154_tasklet_handler(unsigned long data)
{
struct ieee802154_local *local = (struct ieee802154_local *)data;
struct sk_buff *skb;
while ((skb = skb_dequeue(&local->skb_queue))) {
switch (skb->pkt_type) {
case IEEE802154_RX_MSG:
/* Clear skb->pkt_type in order to not confuse kernel
* netstack.
*/
skb->pkt_type = 0;
ieee802154_rx(&local->hw, skb);
break;
default:
WARN(1, "mac802154: Packet is of unknown type %d\n",
skb->pkt_type);
kfree_skb(skb);
break;
}
}
}
struct ieee802154_hw *
ieee802154_alloc_hw(size_t priv_data_len, const struct ieee802154_ops *ops)
{
struct wpan_phy *phy;
struct ieee802154_local *local;
size_t priv_size;
if (!ops || !(ops->xmit_async || ops->xmit_sync) || !ops->ed ||
!ops->start || !ops->stop || !ops->set_channel) {
pr_err("undefined IEEE802.15.4 device operations\n");
return NULL;
}
/* Ensure 32-byte alignment of our private data and hw private data.
* We use the wpan_phy priv data for both our ieee802154_local and for
* the driver's private data
*
* in memory it'll be like this:
*
* +-------------------------+
* | struct wpan_phy |
* +-------------------------+
* | struct ieee802154_local |
* +-------------------------+
* | driver's private data |
* +-------------------------+
*
* Due to ieee802154 layer isn't aware of driver and MAC structures,
* so lets align them here.
*/
priv_size = ALIGN(sizeof(*local), NETDEV_ALIGN) + priv_data_len;
phy = wpan_phy_alloc(priv_size);
if (!phy) {
pr_err("failure to allocate master IEEE802.15.4 device\n");
return NULL;
}
local = wpan_phy_priv(phy);
local->phy = phy;
local->hw.phy = local->phy;
local->hw.priv = (char *)local + ALIGN(sizeof(*local), NETDEV_ALIGN);
local->ops = ops;
INIT_LIST_HEAD(&local->interfaces);
mutex_init(&local->iflist_mtx);
tasklet_init(&local->tasklet,
ieee802154_tasklet_handler,
(unsigned long)local);
skb_queue_head_init(&local->skb_queue);
return &local->hw;
}
EXPORT_SYMBOL(ieee802154_alloc_hw);
void ieee802154_free_hw(struct ieee802154_hw *hw)
{
struct ieee802154_local *local = hw_to_local(hw);
BUG_ON(!list_empty(&local->interfaces));
mutex_destroy(&local->iflist_mtx);
wpan_phy_free(local->phy);
}
EXPORT_SYMBOL(ieee802154_free_hw);
int ieee802154_register_hw(struct ieee802154_hw *hw)
{
struct ieee802154_local *local = hw_to_local(hw);
int rc = -ENOSYS;
local->workqueue =
create_singlethread_workqueue(wpan_phy_name(local->phy));
if (!local->workqueue) {
rc = -ENOMEM;
goto out;
}
wpan_phy_set_dev(local->phy, local->hw.parent);
local->phy->add_iface = mac802154_add_iface;
local->phy->del_iface = mac802154_del_iface;
rc = wpan_phy_register(local->phy);
if (rc < 0)
goto out_wq;
return 0;
out_wq:
destroy_workqueue(local->workqueue);
out:
return rc;
}
EXPORT_SYMBOL(ieee802154_register_hw);
void ieee802154_unregister_hw(struct ieee802154_hw *hw)
{
struct ieee802154_local *local = hw_to_local(hw);
struct ieee802154_sub_if_data *sdata, *next;
tasklet_kill(&local->tasklet);
flush_workqueue(local->workqueue);
destroy_workqueue(local->workqueue);
rtnl_lock();
list_for_each_entry_safe(sdata, next, &local->interfaces, list) {
mutex_lock(&sdata->local->iflist_mtx);
list_del(&sdata->list);
mutex_unlock(&sdata->local->iflist_mtx);
unregister_netdevice(sdata->dev);
}
rtnl_unlock();
wpan_phy_unregister(local->phy);
}
EXPORT_SYMBOL(ieee802154_unregister_hw);
MODULE_DESCRIPTION("IEEE 802.15.4 implementation");
MODULE_LICENSE("GPL v2");

View File

@@ -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.
*
* Written by: * Written by:
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
* Sergey Lapin <slapin@ossfans.org> * Sergey Lapin <slapin@ossfans.org>
@@ -25,14 +21,10 @@
#include <net/mac802154.h> #include <net/mac802154.h>
#include <net/ieee802154_netdev.h> #include <net/ieee802154_netdev.h>
#include <net/wpan-phy.h> #include <net/cfg802154.h>
#include "mac802154.h" #include "ieee802154_i.h"
#include "driver-ops.h"
struct phy_chan_notify_work {
struct work_struct work;
struct net_device *dev;
};
struct hw_addr_filt_notify_work { struct hw_addr_filt_notify_work {
struct work_struct work; struct work_struct work;
@@ -40,24 +32,23 @@ struct hw_addr_filt_notify_work {
unsigned long changed; unsigned long changed;
}; };
static struct mac802154_priv *mac802154_slave_get_priv(struct net_device *dev) static struct ieee802154_local *mac802154_slave_get_priv(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 priv->hw; return sdata->local;
} }
static void hw_addr_notify(struct work_struct *work) static void hw_addr_notify(struct work_struct *work)
{ {
struct hw_addr_filt_notify_work *nw = container_of(work, struct hw_addr_filt_notify_work *nw = container_of(work,
struct hw_addr_filt_notify_work, work); struct hw_addr_filt_notify_work, work);
struct mac802154_priv *hw = mac802154_slave_get_priv(nw->dev); struct ieee802154_local *local = mac802154_slave_get_priv(nw->dev);
int res; int res;
res = hw->ops->set_hw_addr_filt(&hw->hw, res = local->ops->set_hw_addr_filt(&local->hw, &local->hw.hw_filt,
&hw->hw.hw_filt,
nw->changed); nw->changed);
if (res) if (res)
pr_debug("failed changed mask %lx\n", nw->changed); pr_debug("failed changed mask %lx\n", nw->changed);
@@ -67,7 +58,7 @@ static void hw_addr_notify(struct work_struct *work)
static void set_hw_addr_filt(struct net_device *dev, unsigned long changed) static void set_hw_addr_filt(struct net_device *dev, unsigned long changed)
{ {
struct mac802154_sub_if_data *priv = netdev_priv(dev); struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
struct hw_addr_filt_notify_work *work; struct hw_addr_filt_notify_work *work;
work = kzalloc(sizeof(*work), GFP_ATOMIC); work = kzalloc(sizeof(*work), GFP_ATOMIC);
@@ -77,141 +68,110 @@ static void set_hw_addr_filt(struct net_device *dev, unsigned long changed)
INIT_WORK(&work->work, hw_addr_notify); INIT_WORK(&work->work, hw_addr_notify);
work->dev = dev; work->dev = dev;
work->changed = changed; work->changed = changed;
queue_work(priv->hw->dev_workqueue, &work->work); queue_work(sdata->local->workqueue, &work->work);
} }
void mac802154_dev_set_short_addr(struct net_device *dev, __le16 val) void mac802154_dev_set_short_addr(struct net_device *dev, __le16 val)
{ {
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);
spin_lock_bh(&priv->mib_lock); spin_lock_bh(&sdata->mib_lock);
priv->short_addr = val; sdata->short_addr = val;
spin_unlock_bh(&priv->mib_lock); spin_unlock_bh(&sdata->mib_lock);
if ((priv->hw->ops->set_hw_addr_filt) && if ((sdata->local->ops->set_hw_addr_filt) &&
(priv->hw->hw.hw_filt.short_addr != priv->short_addr)) { (sdata->local->hw.hw_filt.short_addr != sdata->short_addr)) {
priv->hw->hw.hw_filt.short_addr = priv->short_addr; sdata->local->hw.hw_filt.short_addr = sdata->short_addr;
set_hw_addr_filt(dev, IEEE802515_AFILT_SADDR_CHANGED); set_hw_addr_filt(dev, IEEE802154_AFILT_SADDR_CHANGED);
} }
} }
__le16 mac802154_dev_get_short_addr(const struct net_device *dev) __le16 mac802154_dev_get_short_addr(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);
__le16 ret; __le16 ret;
BUG_ON(dev->type != ARPHRD_IEEE802154); BUG_ON(dev->type != ARPHRD_IEEE802154);
spin_lock_bh(&priv->mib_lock); spin_lock_bh(&sdata->mib_lock);
ret = priv->short_addr; ret = sdata->short_addr;
spin_unlock_bh(&priv->mib_lock); spin_unlock_bh(&sdata->mib_lock);
return ret; return ret;
} }
void mac802154_dev_set_ieee_addr(struct net_device *dev) void mac802154_dev_set_ieee_addr(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);
struct mac802154_priv *mac = priv->hw; struct ieee802154_local *local = sdata->local;
priv->extended_addr = ieee802154_devaddr_from_raw(dev->dev_addr); sdata->extended_addr = ieee802154_devaddr_from_raw(dev->dev_addr);
if (mac->ops->set_hw_addr_filt && if (local->ops->set_hw_addr_filt &&
mac->hw.hw_filt.ieee_addr != priv->extended_addr) { local->hw.hw_filt.ieee_addr != sdata->extended_addr) {
mac->hw.hw_filt.ieee_addr = priv->extended_addr; local->hw.hw_filt.ieee_addr = sdata->extended_addr;
set_hw_addr_filt(dev, IEEE802515_AFILT_IEEEADDR_CHANGED); set_hw_addr_filt(dev, IEEE802154_AFILT_IEEEADDR_CHANGED);
} }
} }
__le16 mac802154_dev_get_pan_id(const struct net_device *dev) __le16 mac802154_dev_get_pan_id(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);
__le16 ret; __le16 ret;
BUG_ON(dev->type != ARPHRD_IEEE802154); BUG_ON(dev->type != ARPHRD_IEEE802154);
spin_lock_bh(&priv->mib_lock); spin_lock_bh(&sdata->mib_lock);
ret = priv->pan_id; ret = sdata->pan_id;
spin_unlock_bh(&priv->mib_lock); spin_unlock_bh(&sdata->mib_lock);
return ret; return ret;
} }
void mac802154_dev_set_pan_id(struct net_device *dev, __le16 val) void mac802154_dev_set_pan_id(struct net_device *dev, __le16 val)
{ {
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);
spin_lock_bh(&priv->mib_lock); spin_lock_bh(&sdata->mib_lock);
priv->pan_id = val; sdata->pan_id = val;
spin_unlock_bh(&priv->mib_lock); spin_unlock_bh(&sdata->mib_lock);
if ((priv->hw->ops->set_hw_addr_filt) && if ((sdata->local->ops->set_hw_addr_filt) &&
(priv->hw->hw.hw_filt.pan_id != priv->pan_id)) { (sdata->local->hw.hw_filt.pan_id != sdata->pan_id)) {
priv->hw->hw.hw_filt.pan_id = priv->pan_id; sdata->local->hw.hw_filt.pan_id = sdata->pan_id;
set_hw_addr_filt(dev, IEEE802515_AFILT_PANID_CHANGED); set_hw_addr_filt(dev, IEEE802154_AFILT_PANID_CHANGED);
} }
} }
u8 mac802154_dev_get_dsn(const struct net_device *dev) u8 mac802154_dev_get_dsn(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 priv->dsn++; return sdata->dsn++;
}
static void phy_chan_notify(struct work_struct *work)
{
struct phy_chan_notify_work *nw = container_of(work,
struct phy_chan_notify_work, work);
struct mac802154_priv *hw = mac802154_slave_get_priv(nw->dev);
struct mac802154_sub_if_data *priv = netdev_priv(nw->dev);
int res;
mutex_lock(&priv->hw->phy->pib_lock);
res = hw->ops->set_channel(&hw->hw, priv->page, priv->chan);
if (res) {
pr_debug("set_channel failed\n");
} else {
priv->hw->phy->current_channel = priv->chan;
priv->hw->phy->current_page = priv->page;
}
mutex_unlock(&priv->hw->phy->pib_lock);
kfree(nw);
} }
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)
{ {
struct mac802154_sub_if_data *priv = netdev_priv(dev); struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
struct phy_chan_notify_work *work; struct ieee802154_local *local = sdata->local;
int res;
BUG_ON(dev->type != ARPHRD_IEEE802154); BUG_ON(dev->type != ARPHRD_IEEE802154);
spin_lock_bh(&priv->mib_lock); res = drv_set_channel(local, page, chan);
priv->page = page; if (res) {
priv->chan = chan; pr_debug("set_channel failed\n");
spin_unlock_bh(&priv->mib_lock);
mutex_lock(&priv->hw->phy->pib_lock);
if (priv->hw->phy->current_channel != priv->chan ||
priv->hw->phy->current_page != priv->page) {
mutex_unlock(&priv->hw->phy->pib_lock);
work = kzalloc(sizeof(*work), GFP_ATOMIC);
if (!work)
return;
INIT_WORK(&work->work, phy_chan_notify);
work->dev = dev;
queue_work(priv->hw->dev_workqueue, &work->work);
} else { } else {
mutex_unlock(&priv->hw->phy->pib_lock); mutex_lock(&local->phy->pib_lock);
local->phy->current_channel = chan;
local->phy->current_page = page;
mutex_unlock(&local->phy->pib_lock);
} }
} }
@@ -219,14 +179,14 @@ void mac802154_dev_set_page_channel(struct net_device *dev, u8 page, u8 chan)
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)
{ {
struct mac802154_sub_if_data *priv = netdev_priv(dev); struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
int res; int res;
BUG_ON(dev->type != ARPHRD_IEEE802154); BUG_ON(dev->type != ARPHRD_IEEE802154);
mutex_lock(&priv->sec_mtx); mutex_lock(&sdata->sec_mtx);
res = mac802154_llsec_get_params(&priv->sec, params); res = mac802154_llsec_get_params(&sdata->sec, params);
mutex_unlock(&priv->sec_mtx); mutex_unlock(&sdata->sec_mtx);
return res; return res;
} }
@@ -235,14 +195,14 @@ int mac802154_set_params(struct net_device *dev,
const struct ieee802154_llsec_params *params, const struct ieee802154_llsec_params *params,
int changed) int changed)
{ {
struct mac802154_sub_if_data *priv = netdev_priv(dev); struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
int res; int res;
BUG_ON(dev->type != ARPHRD_IEEE802154); BUG_ON(dev->type != ARPHRD_IEEE802154);
mutex_lock(&priv->sec_mtx); mutex_lock(&sdata->sec_mtx);
res = mac802154_llsec_set_params(&priv->sec, params, changed); res = mac802154_llsec_set_params(&sdata->sec, params, changed);
mutex_unlock(&priv->sec_mtx); mutex_unlock(&sdata->sec_mtx);
return res; return res;
} }
@@ -252,14 +212,14 @@ int mac802154_add_key(struct net_device *dev,
const struct ieee802154_llsec_key_id *id, const struct ieee802154_llsec_key_id *id,
const struct ieee802154_llsec_key *key) const struct ieee802154_llsec_key *key)
{ {
struct mac802154_sub_if_data *priv = netdev_priv(dev); struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
int res; int res;
BUG_ON(dev->type != ARPHRD_IEEE802154); BUG_ON(dev->type != ARPHRD_IEEE802154);
mutex_lock(&priv->sec_mtx); mutex_lock(&sdata->sec_mtx);
res = mac802154_llsec_key_add(&priv->sec, id, key); res = mac802154_llsec_key_add(&sdata->sec, id, key);
mutex_unlock(&priv->sec_mtx); mutex_unlock(&sdata->sec_mtx);
return res; return res;
} }
@@ -267,14 +227,14 @@ int mac802154_add_key(struct net_device *dev,
int mac802154_del_key(struct net_device *dev, int mac802154_del_key(struct net_device *dev,
const struct ieee802154_llsec_key_id *id) const struct ieee802154_llsec_key_id *id)
{ {
struct mac802154_sub_if_data *priv = netdev_priv(dev); struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
int res; int res;
BUG_ON(dev->type != ARPHRD_IEEE802154); BUG_ON(dev->type != ARPHRD_IEEE802154);
mutex_lock(&priv->sec_mtx); mutex_lock(&sdata->sec_mtx);
res = mac802154_llsec_key_del(&priv->sec, id); res = mac802154_llsec_key_del(&sdata->sec, id);
mutex_unlock(&priv->sec_mtx); mutex_unlock(&sdata->sec_mtx);
return res; return res;
} }
@@ -283,28 +243,28 @@ int mac802154_del_key(struct net_device *dev,
int mac802154_add_dev(struct net_device *dev, int mac802154_add_dev(struct net_device *dev,
const struct ieee802154_llsec_device *llsec_dev) const struct ieee802154_llsec_device *llsec_dev)
{ {
struct mac802154_sub_if_data *priv = netdev_priv(dev); struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
int res; int res;
BUG_ON(dev->type != ARPHRD_IEEE802154); BUG_ON(dev->type != ARPHRD_IEEE802154);
mutex_lock(&priv->sec_mtx); mutex_lock(&sdata->sec_mtx);
res = mac802154_llsec_dev_add(&priv->sec, llsec_dev); res = mac802154_llsec_dev_add(&sdata->sec, llsec_dev);
mutex_unlock(&priv->sec_mtx); mutex_unlock(&sdata->sec_mtx);
return res; return res;
} }
int mac802154_del_dev(struct net_device *dev, __le64 dev_addr) int mac802154_del_dev(struct net_device *dev, __le64 dev_addr)
{ {
struct mac802154_sub_if_data *priv = netdev_priv(dev); struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
int res; int res;
BUG_ON(dev->type != ARPHRD_IEEE802154); BUG_ON(dev->type != ARPHRD_IEEE802154);
mutex_lock(&priv->sec_mtx); mutex_lock(&sdata->sec_mtx);
res = mac802154_llsec_dev_del(&priv->sec, dev_addr); res = mac802154_llsec_dev_del(&sdata->sec, dev_addr);
mutex_unlock(&priv->sec_mtx); mutex_unlock(&sdata->sec_mtx);
return res; return res;
} }
@@ -314,14 +274,14 @@ int mac802154_add_devkey(struct net_device *dev,
__le64 device_addr, __le64 device_addr,
const struct ieee802154_llsec_device_key *key) const struct ieee802154_llsec_device_key *key)
{ {
struct mac802154_sub_if_data *priv = netdev_priv(dev); struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
int res; int res;
BUG_ON(dev->type != ARPHRD_IEEE802154); BUG_ON(dev->type != ARPHRD_IEEE802154);
mutex_lock(&priv->sec_mtx); mutex_lock(&sdata->sec_mtx);
res = mac802154_llsec_devkey_add(&priv->sec, device_addr, key); res = mac802154_llsec_devkey_add(&sdata->sec, device_addr, key);
mutex_unlock(&priv->sec_mtx); mutex_unlock(&sdata->sec_mtx);
return res; return res;
} }
@@ -330,14 +290,14 @@ int mac802154_del_devkey(struct net_device *dev,
__le64 device_addr, __le64 device_addr,
const struct ieee802154_llsec_device_key *key) const struct ieee802154_llsec_device_key *key)
{ {
struct mac802154_sub_if_data *priv = netdev_priv(dev); struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
int res; int res;
BUG_ON(dev->type != ARPHRD_IEEE802154); BUG_ON(dev->type != ARPHRD_IEEE802154);
mutex_lock(&priv->sec_mtx); mutex_lock(&sdata->sec_mtx);
res = mac802154_llsec_devkey_del(&priv->sec, device_addr, key); res = mac802154_llsec_devkey_del(&sdata->sec, device_addr, key);
mutex_unlock(&priv->sec_mtx); mutex_unlock(&sdata->sec_mtx);
return res; return res;
} }
@@ -346,14 +306,14 @@ int mac802154_del_devkey(struct net_device *dev,
int mac802154_add_seclevel(struct net_device *dev, int mac802154_add_seclevel(struct net_device *dev,
const struct ieee802154_llsec_seclevel *sl) const struct ieee802154_llsec_seclevel *sl)
{ {
struct mac802154_sub_if_data *priv = netdev_priv(dev); struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
int res; int res;
BUG_ON(dev->type != ARPHRD_IEEE802154); BUG_ON(dev->type != ARPHRD_IEEE802154);
mutex_lock(&priv->sec_mtx); mutex_lock(&sdata->sec_mtx);
res = mac802154_llsec_seclevel_add(&priv->sec, sl); res = mac802154_llsec_seclevel_add(&sdata->sec, sl);
mutex_unlock(&priv->sec_mtx); mutex_unlock(&sdata->sec_mtx);
return res; return res;
} }
@@ -361,14 +321,14 @@ int mac802154_add_seclevel(struct net_device *dev,
int mac802154_del_seclevel(struct net_device *dev, int mac802154_del_seclevel(struct net_device *dev,
const struct ieee802154_llsec_seclevel *sl) const struct ieee802154_llsec_seclevel *sl)
{ {
struct mac802154_sub_if_data *priv = netdev_priv(dev); struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
int res; int res;
BUG_ON(dev->type != ARPHRD_IEEE802154); BUG_ON(dev->type != ARPHRD_IEEE802154);
mutex_lock(&priv->sec_mtx); mutex_lock(&sdata->sec_mtx);
res = mac802154_llsec_seclevel_del(&priv->sec, sl); res = mac802154_llsec_seclevel_del(&sdata->sec, sl);
mutex_unlock(&priv->sec_mtx); mutex_unlock(&sdata->sec_mtx);
return res; return res;
} }
@@ -376,28 +336,28 @@ int mac802154_del_seclevel(struct net_device *dev,
void mac802154_lock_table(struct net_device *dev) void mac802154_lock_table(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);
mutex_lock(&priv->sec_mtx); mutex_lock(&sdata->sec_mtx);
} }
void mac802154_get_table(struct net_device *dev, void mac802154_get_table(struct net_device *dev,
struct ieee802154_llsec_table **t) struct ieee802154_llsec_table **t)
{ {
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);
*t = &priv->sec.table; *t = &sdata->sec.table;
} }
void mac802154_unlock_table(struct net_device *dev) void mac802154_unlock_table(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);
mutex_unlock(&priv->sec_mtx); mutex_unlock(&sdata->sec_mtx);
} }

View File

@@ -1,117 +0,0 @@
/*
* 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;
}

View File

@@ -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.
*
* 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>
@@ -23,92 +19,285 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/workqueue.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/crc-ccitt.h> #include <linux/crc-ccitt.h>
#include <asm/unaligned.h>
#include <net/mac802154.h> #include <net/mac802154.h>
#include <net/ieee802154_netdev.h> #include <net/ieee802154_netdev.h>
#include <net/rtnetlink.h>
#include <linux/nl802154.h>
#include "mac802154.h" #include "ieee802154_i.h"
/* The IEEE 802.15.4 standard defines 4 MAC packet types: static int ieee802154_deliver_skb(struct sk_buff *skb)
* - beacon frame
* - MAC command frame
* - acknowledgement frame
* - data frame
*
* and only the data frame should be pushed to the upper layers, other types
* are just internal MAC layer management information. So only data packets
* are going to be sent to the networking queue, all other will be processed
* right here by using the device workqueue.
*/
struct rx_work {
struct sk_buff *skb;
struct work_struct work;
struct ieee802154_dev *dev;
u8 lqi;
};
static void
mac802154_subif_rx(struct ieee802154_dev *hw, struct sk_buff *skb, u8 lqi)
{ {
struct mac802154_priv *priv = mac802154_to_priv(hw); skb->ip_summed = CHECKSUM_UNNECESSARY;
mac_cb(skb)->lqi = lqi;
skb->protocol = htons(ETH_P_IEEE802154); skb->protocol = htons(ETH_P_IEEE802154);
skb_reset_mac_header(skb);
if (!(priv->hw.flags & IEEE802154_HW_OMIT_CKSUM)) { return netif_receive_skb(skb);
u16 crc; }
if (skb->len < 2) { static int
pr_debug("got invalid frame\n"); ieee802154_subif_frame(struct ieee802154_sub_if_data *sdata,
struct sk_buff *skb, const struct ieee802154_hdr *hdr)
{
__le16 span, sshort;
int rc;
pr_debug("getting packet via slave interface %s\n", sdata->dev->name);
spin_lock_bh(&sdata->mib_lock);
span = sdata->pan_id;
sshort = sdata->short_addr;
switch (mac_cb(skb)->dest.mode) {
case IEEE802154_ADDR_NONE:
if (mac_cb(skb)->dest.mode != IEEE802154_ADDR_NONE)
/* FIXME: check if we are PAN coordinator */
skb->pkt_type = PACKET_OTHERHOST;
else
/* ACK comes with both addresses empty */
skb->pkt_type = PACKET_HOST;
break;
case IEEE802154_ADDR_LONG:
if (mac_cb(skb)->dest.pan_id != span &&
mac_cb(skb)->dest.pan_id != cpu_to_le16(IEEE802154_PANID_BROADCAST))
skb->pkt_type = PACKET_OTHERHOST;
else if (mac_cb(skb)->dest.extended_addr == sdata->extended_addr)
skb->pkt_type = PACKET_HOST;
else
skb->pkt_type = PACKET_OTHERHOST;
break;
case IEEE802154_ADDR_SHORT:
if (mac_cb(skb)->dest.pan_id != span &&
mac_cb(skb)->dest.pan_id != cpu_to_le16(IEEE802154_PANID_BROADCAST))
skb->pkt_type = PACKET_OTHERHOST;
else if (mac_cb(skb)->dest.short_addr == sshort)
skb->pkt_type = PACKET_HOST;
else if (mac_cb(skb)->dest.short_addr ==
cpu_to_le16(IEEE802154_ADDR_BROADCAST))
skb->pkt_type = PACKET_BROADCAST;
else
skb->pkt_type = PACKET_OTHERHOST;
break;
default:
spin_unlock_bh(&sdata->mib_lock);
pr_debug("invalid dest mode\n");
kfree_skb(skb);
return NET_RX_DROP;
}
spin_unlock_bh(&sdata->mib_lock);
skb->dev = sdata->dev;
rc = mac802154_llsec_decrypt(&sdata->sec, skb);
if (rc) {
pr_debug("decryption failed: %i\n", rc);
goto fail; goto fail;
} }
crc = crc_ccitt(0, skb->data, skb->len);
if (crc) { sdata->dev->stats.rx_packets++;
pr_debug("CRC mismatch\n"); sdata->dev->stats.rx_bytes += skb->len;
switch (mac_cb(skb)->type) {
case IEEE802154_FC_TYPE_DATA:
return ieee802154_deliver_skb(skb);
default:
pr_warn("ieee802154: bad frame received (type = %d)\n",
mac_cb(skb)->type);
goto fail; goto fail;
} }
skb_trim(skb, skb->len - 2); /* CRC */
}
mac802154_monitors_rx(priv, skb);
mac802154_wpans_rx(priv, skb);
return;
fail: fail:
kfree_skb(skb); kfree_skb(skb);
return NET_RX_DROP;
} }
static void mac802154_rx_worker(struct work_struct *work) static void
ieee802154_print_addr(const char *name, const struct ieee802154_addr *addr)
{ {
struct rx_work *rw = container_of(work, struct rx_work, work); if (addr->mode == IEEE802154_ADDR_NONE)
pr_debug("%s not present\n", name);
mac802154_subif_rx(rw->dev, rw->skb, rw->lqi); pr_debug("%s PAN ID: %04x\n", name, le16_to_cpu(addr->pan_id));
kfree(rw); if (addr->mode == IEEE802154_ADDR_SHORT) {
pr_debug("%s is short: %04x\n", name,
le16_to_cpu(addr->short_addr));
} else {
u64 hw = swab64((__force u64)addr->extended_addr);
pr_debug("%s is hardware: %8phC\n", name, &hw);
} }
}
static int
ieee802154_parse_frame_start(struct sk_buff *skb, struct ieee802154_hdr *hdr)
{
int hlen;
struct ieee802154_mac_cb *cb = mac_cb_init(skb);
skb_reset_mac_header(skb);
hlen = ieee802154_hdr_pull(skb, hdr);
if (hlen < 0)
return -EINVAL;
skb->mac_len = hlen;
pr_debug("fc: %04x dsn: %02x\n", le16_to_cpup((__le16 *)&hdr->fc),
hdr->seq);
cb->type = hdr->fc.type;
cb->ackreq = hdr->fc.ack_request;
cb->secen = hdr->fc.security_enabled;
ieee802154_print_addr("destination", &hdr->dest);
ieee802154_print_addr("source", &hdr->source);
cb->source = hdr->source;
cb->dest = hdr->dest;
if (hdr->fc.security_enabled) {
u64 key;
pr_debug("seclevel %i\n", hdr->sec.level);
switch (hdr->sec.key_id_mode) {
case IEEE802154_SCF_KEY_IMPLICIT:
pr_debug("implicit key\n");
break;
case IEEE802154_SCF_KEY_INDEX:
pr_debug("key %02x\n", hdr->sec.key_id);
break;
case IEEE802154_SCF_KEY_SHORT_INDEX:
pr_debug("key %04x:%04x %02x\n",
le32_to_cpu(hdr->sec.short_src) >> 16,
le32_to_cpu(hdr->sec.short_src) & 0xffff,
hdr->sec.key_id);
break;
case IEEE802154_SCF_KEY_HW_INDEX:
key = swab64((__force u64)hdr->sec.extended_src);
pr_debug("key source %8phC %02x\n", &key,
hdr->sec.key_id);
break;
}
}
return 0;
}
static void
__ieee802154_rx_handle_packet(struct ieee802154_local *local,
struct sk_buff *skb)
{
int ret;
struct ieee802154_sub_if_data *sdata;
struct ieee802154_hdr hdr;
ret = ieee802154_parse_frame_start(skb, &hdr);
if (ret) {
pr_debug("got invalid frame\n");
kfree_skb(skb);
return;
}
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
if (sdata->type != IEEE802154_DEV_WPAN ||
!netif_running(sdata->dev))
continue;
ieee802154_subif_frame(sdata, skb, &hdr);
skb = NULL;
break;
}
if (skb)
kfree_skb(skb);
}
static void
ieee802154_monitors_rx(struct ieee802154_local *local, struct sk_buff *skb)
{
struct sk_buff *skb2;
struct ieee802154_sub_if_data *sdata;
skb_reset_mac_header(skb);
skb->ip_summed = CHECKSUM_UNNECESSARY;
skb->pkt_type = PACKET_OTHERHOST;
skb->protocol = htons(ETH_P_IEEE802154);
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
if (sdata->type != IEEE802154_DEV_MONITOR)
continue;
if (!ieee802154_sdata_running(sdata))
continue;
skb2 = skb_clone(skb, GFP_ATOMIC);
if (skb2) {
skb2->dev = sdata->dev;
ieee802154_deliver_skb(skb2);
sdata->dev->stats.rx_packets++;
sdata->dev->stats.rx_bytes += skb->len;
}
}
}
void ieee802154_rx(struct ieee802154_hw *hw, struct sk_buff *skb)
{
struct ieee802154_local *local = hw_to_local(hw);
u16 crc;
WARN_ON_ONCE(softirq_count() == 0);
/* TODO: When a transceiver omits the checksum here, we
* add an own calculated one. This is currently an ugly
* solution because the monitor needs a crc here.
*/
if (local->hw.flags & IEEE802154_HW_RX_OMIT_CKSUM) {
crc = crc_ccitt(0, skb->data, skb->len);
put_unaligned_le16(crc, skb_put(skb, 2));
}
rcu_read_lock();
ieee802154_monitors_rx(local, skb);
/* Check if transceiver doesn't validate the checksum.
* If not we validate the checksum here.
*/
if (local->hw.flags & IEEE802154_HW_RX_DROP_BAD_CKSUM) {
crc = crc_ccitt(0, skb->data, skb->len);
if (crc) {
rcu_read_unlock();
kfree_skb(skb);
return;
}
}
/* remove crc */
skb_trim(skb, skb->len - 2);
__ieee802154_rx_handle_packet(local, skb);
rcu_read_unlock();
}
EXPORT_SYMBOL(ieee802154_rx);
void void
ieee802154_rx_irqsafe(struct ieee802154_dev *dev, struct sk_buff *skb, u8 lqi) ieee802154_rx_irqsafe(struct ieee802154_hw *hw, struct sk_buff *skb, u8 lqi)
{ {
struct mac802154_priv *priv = mac802154_to_priv(dev); struct ieee802154_local *local = hw_to_local(hw);
struct rx_work *work;
if (!skb) mac_cb(skb)->lqi = lqi;
return; skb->pkt_type = IEEE802154_RX_MSG;
skb_queue_tail(&local->skb_queue, skb);
work = kzalloc(sizeof(*work), GFP_ATOMIC); tasklet_schedule(&local->tasklet);
if (!work)
return;
INIT_WORK(&work->work, mac802154_rx_worker);
work->skb = skb;
work->dev = dev;
work->lqi = lqi;
queue_work(priv->dev_workqueue, &work->work);
} }
EXPORT_SYMBOL(ieee802154_rx_irqsafe); EXPORT_SYMBOL(ieee802154_rx_irqsafe);

View File

@@ -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.
*
* Written by: * Written by:
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
* Sergey Lapin <slapin@ossfans.org> * Sergey Lapin <slapin@ossfans.org>
@@ -24,106 +20,98 @@
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/if_arp.h> #include <linux/if_arp.h>
#include <linux/crc-ccitt.h> #include <linux/crc-ccitt.h>
#include <asm/unaligned.h>
#include <net/rtnetlink.h>
#include <net/ieee802154_netdev.h> #include <net/ieee802154_netdev.h>
#include <net/mac802154.h> #include <net/mac802154.h>
#include <net/wpan-phy.h> #include <net/cfg802154.h>
#include "mac802154.h" #include "ieee802154_i.h"
#include "driver-ops.h"
/* IEEE 802.15.4 transceivers can sleep during the xmit session, so process /* IEEE 802.15.4 transceivers can sleep during the xmit session, so process
* packets through the workqueue. * packets through the workqueue.
*/ */
struct xmit_work { struct ieee802154_xmit_cb {
struct sk_buff *skb; struct sk_buff *skb;
struct work_struct work; struct work_struct work;
struct mac802154_priv *priv; struct ieee802154_local *local;
u8 chan;
u8 page;
}; };
static void mac802154_xmit_worker(struct work_struct *work) static struct ieee802154_xmit_cb ieee802154_xmit_cb;
static void ieee802154_xmit_worker(struct work_struct *work)
{ {
struct xmit_work *xw = container_of(work, struct xmit_work, work); struct ieee802154_xmit_cb *cb =
struct mac802154_sub_if_data *sdata; container_of(work, struct ieee802154_xmit_cb, work);
struct ieee802154_local *local = cb->local;
struct sk_buff *skb = cb->skb;
struct net_device *dev = skb->dev;
int res; int res;
mutex_lock(&xw->priv->phy->pib_lock); rtnl_lock();
if (xw->priv->phy->current_channel != xw->chan ||
xw->priv->phy->current_page != xw->page) {
res = xw->priv->ops->set_channel(&xw->priv->hw,
xw->page,
xw->chan);
if (res) {
pr_debug("set_channel failed\n");
goto out;
}
xw->priv->phy->current_channel = xw->chan; /* check if ifdown occurred while schedule */
xw->priv->phy->current_page = xw->page; if (!netif_running(dev))
} goto err_tx;
res = xw->priv->ops->xmit(&xw->priv->hw, xw->skb); res = drv_xmit_sync(local, skb);
if (res) if (res)
pr_debug("transmission failed\n"); goto err_tx;
out: ieee802154_xmit_complete(&local->hw, skb);
mutex_unlock(&xw->priv->phy->pib_lock);
dev->stats.tx_packets++;
dev->stats.tx_bytes += skb->len;
rtnl_unlock();
return;
err_tx:
/* Restart the netif queue on each sub_if_data object. */ /* Restart the netif queue on each sub_if_data object. */
rcu_read_lock(); ieee802154_wake_queue(&local->hw);
list_for_each_entry_rcu(sdata, &xw->priv->slaves, list) rtnl_unlock();
netif_wake_queue(sdata->dev);
rcu_read_unlock();
dev_kfree_skb(xw->skb);
kfree(xw);
}
netdev_tx_t mac802154_tx(struct mac802154_priv *priv, struct sk_buff *skb,
u8 page, u8 chan)
{
struct xmit_work *work;
struct mac802154_sub_if_data *sdata;
if (!(priv->phy->channels_supported[page] & (1 << chan))) {
WARN_ON(1);
goto err_tx;
}
mac802154_monitors_rx(mac802154_to_priv(&priv->hw), skb);
if (!(priv->hw.flags & IEEE802154_HW_OMIT_CKSUM)) {
u16 crc = crc_ccitt(0, skb->data, skb->len);
u8 *data = skb_put(skb, 2);
data[0] = crc & 0xff;
data[1] = crc >> 8;
}
if (skb_cow_head(skb, priv->hw.extra_tx_headroom))
goto err_tx;
work = kzalloc(sizeof(*work), GFP_ATOMIC);
if (!work) {
kfree_skb(skb); kfree_skb(skb);
return NETDEV_TX_BUSY; netdev_dbg(dev, "transmission failed\n");
} }
static netdev_tx_t
ieee802154_tx(struct ieee802154_local *local, struct sk_buff *skb)
{
struct net_device *dev = skb->dev;
int ret;
if (!(local->hw.flags & IEEE802154_HW_TX_OMIT_CKSUM)) {
u16 crc = crc_ccitt(0, skb->data, skb->len);
put_unaligned_le16(crc, skb_put(skb, 2));
}
if (skb_cow_head(skb, local->hw.extra_tx_headroom))
goto err_tx;
/* Stop the netif queue on each sub_if_data object. */ /* Stop the netif queue on each sub_if_data object. */
rcu_read_lock(); ieee802154_stop_queue(&local->hw);
list_for_each_entry_rcu(sdata, &priv->slaves, list)
netif_stop_queue(sdata->dev);
rcu_read_unlock();
INIT_WORK(&work->work, mac802154_xmit_worker); /* async is priority, otherwise sync is fallback */
work->skb = skb; if (local->ops->xmit_async) {
work->priv = priv; ret = drv_xmit_async(local, skb);
work->page = page; if (ret) {
work->chan = chan; ieee802154_wake_queue(&local->hw);
goto err_tx;
}
queue_work(priv->dev_workqueue, &work->work); dev->stats.tx_packets++;
dev->stats.tx_bytes += skb->len;
} else {
INIT_WORK(&ieee802154_xmit_cb.work, ieee802154_xmit_worker);
ieee802154_xmit_cb.skb = skb;
ieee802154_xmit_cb.local = local;
queue_work(local->workqueue, &ieee802154_xmit_cb.work);
}
return NETDEV_TX_OK; return NETDEV_TX_OK;
@@ -131,3 +119,31 @@ err_tx:
kfree_skb(skb); kfree_skb(skb);
return NETDEV_TX_OK; return NETDEV_TX_OK;
} }
netdev_tx_t
ieee802154_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
skb->skb_iif = dev->ifindex;
return ieee802154_tx(sdata->local, skb);
}
netdev_tx_t
ieee802154_subif_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
int rc;
rc = mac802154_llsec_encrypt(&sdata->sec, skb);
if (rc) {
netdev_warn(dev, "encryption failed: %i\n", rc);
kfree_skb(skb);
return NETDEV_TX_OK;
}
skb->skb_iif = dev->ifindex;
return ieee802154_tx(sdata->local, skb);
}

55
net/mac802154/util.c Normal file
View File

@@ -0,0 +1,55 @@
/* 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/mac80211/util.c
*/
#include "ieee802154_i.h"
void ieee802154_wake_queue(struct ieee802154_hw *hw)
{
struct ieee802154_local *local = hw_to_local(hw);
struct ieee802154_sub_if_data *sdata;
rcu_read_lock();
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
if (!sdata->dev)
continue;
netif_wake_queue(sdata->dev);
}
rcu_read_unlock();
}
EXPORT_SYMBOL(ieee802154_wake_queue);
void ieee802154_stop_queue(struct ieee802154_hw *hw)
{
struct ieee802154_local *local = hw_to_local(hw);
struct ieee802154_sub_if_data *sdata;
rcu_read_lock();
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
if (!sdata->dev)
continue;
netif_stop_queue(sdata->dev);
}
rcu_read_unlock();
}
EXPORT_SYMBOL(ieee802154_stop_queue);
void ieee802154_xmit_complete(struct ieee802154_hw *hw, struct sk_buff *skb)
{
ieee802154_wake_queue(hw);
consume_skb(skb);
}
EXPORT_SYMBOL(ieee802154_xmit_complete);

View File

@@ -1,599 +0,0 @@
/*
* Copyright 2007-2012 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/module.h>
#include <linux/if_arp.h>
#include <net/rtnetlink.h>
#include <linux/nl802154.h>
#include <net/af_ieee802154.h>
#include <net/mac802154.h>
#include <net/ieee802154_netdev.h>
#include <net/ieee802154.h>
#include <net/wpan-phy.h>
#include "mac802154.h"
static int mac802154_wpan_update_llsec(struct net_device *dev)
{
struct mac802154_sub_if_data *priv = netdev_priv(dev);
struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
int rc = 0;
if (ops->llsec) {
struct ieee802154_llsec_params params;
int changed = 0;
params.pan_id = priv->pan_id;
changed |= IEEE802154_LLSEC_PARAM_PAN_ID;
params.hwaddr = priv->extended_addr;
changed |= IEEE802154_LLSEC_PARAM_HWADDR;
rc = ops->llsec->set_params(dev, &params, changed);
}
return rc;
}
static int
mac802154_wpan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
struct mac802154_sub_if_data *priv = netdev_priv(dev);
struct sockaddr_ieee802154 *sa =
(struct sockaddr_ieee802154 *)&ifr->ifr_addr;
int err = -ENOIOCTLCMD;
spin_lock_bh(&priv->mib_lock);
switch (cmd) {
case SIOCGIFADDR:
{
u16 pan_id, short_addr;
pan_id = le16_to_cpu(priv->pan_id);
short_addr = le16_to_cpu(priv->short_addr);
if (pan_id == IEEE802154_PANID_BROADCAST ||
short_addr == IEEE802154_ADDR_BROADCAST) {
err = -EADDRNOTAVAIL;
break;
}
sa->family = AF_IEEE802154;
sa->addr.addr_type = IEEE802154_ADDR_SHORT;
sa->addr.pan_id = pan_id;
sa->addr.short_addr = short_addr;
err = 0;
break;
}
case SIOCSIFADDR:
dev_warn(&dev->dev,
"Using DEBUGing ioctl SIOCSIFADDR isn't recommended!\n");
if (sa->family != AF_IEEE802154 ||
sa->addr.addr_type != IEEE802154_ADDR_SHORT ||
sa->addr.pan_id == IEEE802154_PANID_BROADCAST ||
sa->addr.short_addr == IEEE802154_ADDR_BROADCAST ||
sa->addr.short_addr == IEEE802154_ADDR_UNDEF) {
err = -EINVAL;
break;
}
priv->pan_id = cpu_to_le16(sa->addr.pan_id);
priv->short_addr = cpu_to_le16(sa->addr.short_addr);
err = mac802154_wpan_update_llsec(dev);
break;
}
spin_unlock_bh(&priv->mib_lock);
return err;
}
static int mac802154_wpan_mac_addr(struct net_device *dev, void *p)
{
struct sockaddr *addr = p;
if (netif_running(dev))
return -EBUSY;
/* FIXME: validate addr */
memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
mac802154_dev_set_ieee_addr(dev);
return mac802154_wpan_update_llsec(dev);
}
int mac802154_set_mac_params(struct net_device *dev,
const struct ieee802154_mac_params *params)
{
struct mac802154_sub_if_data *priv = netdev_priv(dev);
mutex_lock(&priv->hw->slaves_mtx);
priv->mac_params = *params;
mutex_unlock(&priv->hw->slaves_mtx);
return 0;
}
void mac802154_get_mac_params(struct net_device *dev,
struct ieee802154_mac_params *params)
{
struct mac802154_sub_if_data *priv = netdev_priv(dev);
mutex_lock(&priv->hw->slaves_mtx);
*params = priv->mac_params;
mutex_unlock(&priv->hw->slaves_mtx);
}
static int mac802154_wpan_open(struct net_device *dev)
{
int rc;
struct mac802154_sub_if_data *priv = netdev_priv(dev);
struct wpan_phy *phy = priv->hw->phy;
rc = mac802154_slave_open(dev);
if (rc < 0)
return rc;
mutex_lock(&phy->pib_lock);
if (phy->set_txpower) {
rc = phy->set_txpower(phy, priv->mac_params.transmit_power);
if (rc < 0)
goto out;
}
if (phy->set_lbt) {
rc = phy->set_lbt(phy, priv->mac_params.lbt);
if (rc < 0)
goto out;
}
if (phy->set_cca_mode) {
rc = phy->set_cca_mode(phy, priv->mac_params.cca_mode);
if (rc < 0)
goto out;
}
if (phy->set_cca_ed_level) {
rc = phy->set_cca_ed_level(phy, priv->mac_params.cca_ed_level);
if (rc < 0)
goto out;
}
if (phy->set_csma_params) {
rc = phy->set_csma_params(phy, priv->mac_params.min_be,
priv->mac_params.max_be,
priv->mac_params.csma_retries);
if (rc < 0)
goto out;
}
if (phy->set_frame_retries) {
rc = phy->set_frame_retries(phy,
priv->mac_params.frame_retries);
if (rc < 0)
goto out;
}
mutex_unlock(&phy->pib_lock);
return 0;
out:
mutex_unlock(&phy->pib_lock);
return rc;
}
static int mac802154_set_header_security(struct mac802154_sub_if_data *priv,
struct ieee802154_hdr *hdr,
const struct ieee802154_mac_cb *cb)
{
struct ieee802154_llsec_params params;
u8 level;
mac802154_llsec_get_params(&priv->sec, &params);
if (!params.enabled && cb->secen_override && cb->secen)
return -EINVAL;
if (!params.enabled ||
(cb->secen_override && !cb->secen) ||
!params.out_level)
return 0;
if (cb->seclevel_override && !cb->seclevel)
return -EINVAL;
level = cb->seclevel_override ? cb->seclevel : params.out_level;
hdr->fc.security_enabled = 1;
hdr->sec.level = level;
hdr->sec.key_id_mode = params.out_key.mode;
if (params.out_key.mode == IEEE802154_SCF_KEY_SHORT_INDEX)
hdr->sec.short_src = params.out_key.short_source;
else if (params.out_key.mode == IEEE802154_SCF_KEY_HW_INDEX)
hdr->sec.extended_src = params.out_key.extended_source;
hdr->sec.key_id = params.out_key.id;
return 0;
}
static int mac802154_header_create(struct sk_buff *skb,
struct net_device *dev,
unsigned short type,
const void *daddr,
const void *saddr,
unsigned len)
{
struct ieee802154_hdr hdr;
struct mac802154_sub_if_data *priv = netdev_priv(dev);
struct ieee802154_mac_cb *cb = mac_cb(skb);
int hlen;
if (!daddr)
return -EINVAL;
memset(&hdr.fc, 0, sizeof(hdr.fc));
hdr.fc.type = cb->type;
hdr.fc.security_enabled = cb->secen;
hdr.fc.ack_request = cb->ackreq;
hdr.seq = ieee802154_mlme_ops(dev)->get_dsn(dev);
if (mac802154_set_header_security(priv, &hdr, cb) < 0)
return -EINVAL;
if (!saddr) {
spin_lock_bh(&priv->mib_lock);
if (priv->short_addr == cpu_to_le16(IEEE802154_ADDR_BROADCAST) ||
priv->short_addr == cpu_to_le16(IEEE802154_ADDR_UNDEF) ||
priv->pan_id == cpu_to_le16(IEEE802154_PANID_BROADCAST)) {
hdr.source.mode = IEEE802154_ADDR_LONG;
hdr.source.extended_addr = priv->extended_addr;
} else {
hdr.source.mode = IEEE802154_ADDR_SHORT;
hdr.source.short_addr = priv->short_addr;
}
hdr.source.pan_id = priv->pan_id;
spin_unlock_bh(&priv->mib_lock);
} else {
hdr.source = *(const struct ieee802154_addr *)saddr;
}
hdr.dest = *(const struct ieee802154_addr *)daddr;
hlen = ieee802154_hdr_push(skb, &hdr);
if (hlen < 0)
return -EINVAL;
skb_reset_mac_header(skb);
skb->mac_len = hlen;
if (len > ieee802154_max_payload(&hdr))
return -EMSGSIZE;
return hlen;
}
static int
mac802154_header_parse(const struct sk_buff *skb, unsigned char *haddr)
{
struct ieee802154_hdr hdr;
struct ieee802154_addr *addr = (struct ieee802154_addr *)haddr;
if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0) {
pr_debug("malformed packet\n");
return 0;
}
*addr = hdr.source;
return sizeof(*addr);
}
static netdev_tx_t
mac802154_wpan_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct mac802154_sub_if_data *priv;
u8 chan, page;
int rc;
priv = netdev_priv(dev);
spin_lock_bh(&priv->mib_lock);
chan = priv->chan;
page = priv->page;
spin_unlock_bh(&priv->mib_lock);
if (chan == MAC802154_CHAN_NONE ||
page >= WPAN_NUM_PAGES ||
chan >= WPAN_NUM_CHANNELS) {
kfree_skb(skb);
return NETDEV_TX_OK;
}
rc = mac802154_llsec_encrypt(&priv->sec, skb);
if (rc) {
pr_warn("encryption failed: %i\n", rc);
kfree_skb(skb);
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);
}
static struct header_ops mac802154_header_ops = {
.create = mac802154_header_create,
.parse = mac802154_header_parse,
};
static const struct net_device_ops mac802154_wpan_ops = {
.ndo_open = mac802154_wpan_open,
.ndo_stop = mac802154_slave_close,
.ndo_start_xmit = mac802154_wpan_xmit,
.ndo_do_ioctl = mac802154_wpan_ioctl,
.ndo_set_mac_address = mac802154_wpan_mac_addr,
};
static void mac802154_wpan_free(struct net_device *dev)
{
struct mac802154_sub_if_data *priv = netdev_priv(dev);
mac802154_llsec_destroy(&priv->sec);
free_netdev(dev);
}
void mac802154_wpan_setup(struct net_device *dev)
{
struct mac802154_sub_if_data *priv;
dev->addr_len = IEEE802154_ADDR_LEN;
memset(dev->broadcast, 0xff, IEEE802154_ADDR_LEN);
dev->hard_header_len = MAC802154_FRAME_HARD_HEADER_LEN;
dev->header_ops = &mac802154_header_ops;
dev->needed_tailroom = 2 + 16; /* FCS + MIC */
dev->mtu = IEEE802154_MTU;
dev->tx_queue_len = 300;
dev->type = ARPHRD_IEEE802154;
dev->flags = IFF_NOARP | IFF_BROADCAST;
dev->watchdog_timeo = 0;
dev->destructor = mac802154_wpan_free;
dev->netdev_ops = &mac802154_wpan_ops;
dev->ml_priv = &mac802154_mlme_wpan;
priv = netdev_priv(dev);
priv->type = IEEE802154_DEV_WPAN;
priv->chan = MAC802154_CHAN_NONE;
priv->page = 0;
spin_lock_init(&priv->mib_lock);
mutex_init(&priv->sec_mtx);
get_random_bytes(&priv->bsn, 1);
get_random_bytes(&priv->dsn, 1);
/* defaults per 802.15.4-2011 */
priv->mac_params.min_be = 3;
priv->mac_params.max_be = 5;
priv->mac_params.csma_retries = 4;
priv->mac_params.frame_retries = -1; /* for compatibility, actual default is 3 */
priv->pan_id = cpu_to_le16(IEEE802154_PANID_BROADCAST);
priv->short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST);
mac802154_llsec_init(&priv->sec);
}
static int mac802154_process_data(struct net_device *dev, struct sk_buff *skb)
{
return netif_rx_ni(skb);
}
static int
mac802154_subif_frame(struct mac802154_sub_if_data *sdata, struct sk_buff *skb,
const struct ieee802154_hdr *hdr)
{
__le16 span, sshort;
int rc;
pr_debug("getting packet via slave interface %s\n", sdata->dev->name);
spin_lock_bh(&sdata->mib_lock);
span = sdata->pan_id;
sshort = sdata->short_addr;
switch (mac_cb(skb)->dest.mode) {
case IEEE802154_ADDR_NONE:
if (mac_cb(skb)->dest.mode != IEEE802154_ADDR_NONE)
/* FIXME: check if we are PAN coordinator */
skb->pkt_type = PACKET_OTHERHOST;
else
/* ACK comes with both addresses empty */
skb->pkt_type = PACKET_HOST;
break;
case IEEE802154_ADDR_LONG:
if (mac_cb(skb)->dest.pan_id != span &&
mac_cb(skb)->dest.pan_id != cpu_to_le16(IEEE802154_PANID_BROADCAST))
skb->pkt_type = PACKET_OTHERHOST;
else if (mac_cb(skb)->dest.extended_addr == sdata->extended_addr)
skb->pkt_type = PACKET_HOST;
else
skb->pkt_type = PACKET_OTHERHOST;
break;
case IEEE802154_ADDR_SHORT:
if (mac_cb(skb)->dest.pan_id != span &&
mac_cb(skb)->dest.pan_id != cpu_to_le16(IEEE802154_PANID_BROADCAST))
skb->pkt_type = PACKET_OTHERHOST;
else if (mac_cb(skb)->dest.short_addr == sshort)
skb->pkt_type = PACKET_HOST;
else if (mac_cb(skb)->dest.short_addr ==
cpu_to_le16(IEEE802154_ADDR_BROADCAST))
skb->pkt_type = PACKET_BROADCAST;
else
skb->pkt_type = PACKET_OTHERHOST;
break;
default:
spin_unlock_bh(&sdata->mib_lock);
pr_debug("invalid dest mode\n");
kfree_skb(skb);
return NET_RX_DROP;
}
spin_unlock_bh(&sdata->mib_lock);
skb->dev = sdata->dev;
rc = mac802154_llsec_decrypt(&sdata->sec, skb);
if (rc) {
pr_debug("decryption failed: %i\n", rc);
goto fail;
}
sdata->dev->stats.rx_packets++;
sdata->dev->stats.rx_bytes += skb->len;
switch (mac_cb(skb)->type) {
case IEEE802154_FC_TYPE_DATA:
return mac802154_process_data(sdata->dev, skb);
default:
pr_warn("ieee802154: bad frame received (type = %d)\n",
mac_cb(skb)->type);
goto fail;
}
fail:
kfree_skb(skb);
return NET_RX_DROP;
}
static void mac802154_print_addr(const char *name,
const struct ieee802154_addr *addr)
{
if (addr->mode == IEEE802154_ADDR_NONE)
pr_debug("%s not present\n", name);
pr_debug("%s PAN ID: %04x\n", name, le16_to_cpu(addr->pan_id));
if (addr->mode == IEEE802154_ADDR_SHORT) {
pr_debug("%s is short: %04x\n", name,
le16_to_cpu(addr->short_addr));
} else {
u64 hw = swab64((__force u64) addr->extended_addr);
pr_debug("%s is hardware: %8phC\n", name, &hw);
}
}
static int mac802154_parse_frame_start(struct sk_buff *skb,
struct ieee802154_hdr *hdr)
{
int hlen;
struct ieee802154_mac_cb *cb = mac_cb_init(skb);
hlen = ieee802154_hdr_pull(skb, hdr);
if (hlen < 0)
return -EINVAL;
skb->mac_len = hlen;
pr_debug("fc: %04x dsn: %02x\n", le16_to_cpup((__le16 *)&hdr->fc),
hdr->seq);
cb->type = hdr->fc.type;
cb->ackreq = hdr->fc.ack_request;
cb->secen = hdr->fc.security_enabled;
mac802154_print_addr("destination", &hdr->dest);
mac802154_print_addr("source", &hdr->source);
cb->source = hdr->source;
cb->dest = hdr->dest;
if (hdr->fc.security_enabled) {
u64 key;
pr_debug("seclevel %i\n", hdr->sec.level);
switch (hdr->sec.key_id_mode) {
case IEEE802154_SCF_KEY_IMPLICIT:
pr_debug("implicit key\n");
break;
case IEEE802154_SCF_KEY_INDEX:
pr_debug("key %02x\n", hdr->sec.key_id);
break;
case IEEE802154_SCF_KEY_SHORT_INDEX:
pr_debug("key %04x:%04x %02x\n",
le32_to_cpu(hdr->sec.short_src) >> 16,
le32_to_cpu(hdr->sec.short_src) & 0xffff,
hdr->sec.key_id);
break;
case IEEE802154_SCF_KEY_HW_INDEX:
key = swab64((__force u64) hdr->sec.extended_src);
pr_debug("key source %8phC %02x\n", &key,
hdr->sec.key_id);
break;
}
}
return 0;
}
void mac802154_wpans_rx(struct mac802154_priv *priv, struct sk_buff *skb)
{
int ret;
struct mac802154_sub_if_data *sdata;
struct ieee802154_hdr hdr;
ret = mac802154_parse_frame_start(skb, &hdr);
if (ret) {
pr_debug("got invalid frame\n");
kfree_skb(skb);
return;
}
rcu_read_lock();
list_for_each_entry_rcu(sdata, &priv->slaves, list) {
if (sdata->type != IEEE802154_DEV_WPAN ||
!netif_running(sdata->dev))
continue;
mac802154_subif_frame(sdata, skb, &hdr);
skb = NULL;
break;
}
rcu_read_unlock();
if (skb)
kfree_skb(skb);
}