diff options
Diffstat (limited to 'net/mac802154')
-rw-r--r-- | net/mac802154/Kconfig | 16 | ||||
-rw-r--r-- | net/mac802154/Makefile | 2 | ||||
-rw-r--r-- | net/mac802154/ieee802154_dev.c | 294 | ||||
-rw-r--r-- | net/mac802154/mac802154.h | 109 | ||||
-rw-r--r-- | net/mac802154/mac_cmd.c | 45 | ||||
-rw-r--r-- | net/mac802154/mib.c | 93 | ||||
-rw-r--r-- | net/mac802154/monitor.c | 116 | ||||
-rw-r--r-- | net/mac802154/rx.c | 114 | ||||
-rw-r--r-- | net/mac802154/tx.c | 116 |
9 files changed, 905 insertions, 0 deletions
diff --git a/net/mac802154/Kconfig b/net/mac802154/Kconfig new file mode 100644 index 000000000000..a967ddaa4e2f --- /dev/null +++ b/net/mac802154/Kconfig @@ -0,0 +1,16 @@ +config MAC802154 + tristate "Generic IEEE 802.15.4 Soft Networking Stack (mac802154)" + depends on IEEE802154 && EXPERIMENTAL + select CRC_CCITT + ---help--- + This option enables the hardware independent IEEE 802.15.4 + networking stack for SoftMAC devices (the ones implementing + only PHY level of IEEE 802.15.4 standard). + + Note: this implementation is neither certified, nor feature + complete! Compatibility with other implementations hasn't + been tested yet! + + 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 + module. diff --git a/net/mac802154/Makefile b/net/mac802154/Makefile new file mode 100644 index 000000000000..ec1bd3fc1273 --- /dev/null +++ b/net/mac802154/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_MAC802154) += mac802154.o +mac802154-objs := ieee802154_dev.o rx.o tx.o mac_cmd.o mib.o monitor.o diff --git a/net/mac802154/ieee802154_dev.c b/net/mac802154/ieee802154_dev.c new file mode 100644 index 000000000000..e3edfb0661b0 --- /dev/null +++ b/net/mac802154/ieee802154_dev.c @@ -0,0 +1,294 @@ +/* + * 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/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_priv *ipriv = priv->hw; + int res = 0; + + if (ipriv->open_count++ == 0) { + res = ipriv->ops->start(&ipriv->hw); + WARN_ON(res); + if (res) + goto err; + } + + if (ipriv->ops->ieee_addr) { + res = ipriv->ops->ieee_addr(&ipriv->hw, dev->dev_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; + + netif_stop_queue(dev); + + 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, mac802154_monitor_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); +} + +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) { + printk(KERN_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) { + printk(KERN_ERR + "failure to allocate master IEEE802.15.4 device\n"); + return NULL; + } + + priv = wpan_phy_priv(phy); + priv->hw.phy = priv->phy = 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)); + + wpan_phy_free(priv->phy); + + mutex_destroy(&priv->slaves_mtx); +} +EXPORT_SYMBOL(ieee802154_free_device); + +int ieee802154_register_device(struct ieee802154_dev *dev) +{ + struct mac802154_priv *priv = mac802154_to_priv(dev); + int rc = -ENOMEM; + + priv->dev_workqueue = + create_singlethread_workqueue(wpan_phy_name(priv->phy)); + if (!priv->dev_workqueue) + 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"); diff --git a/net/mac802154/mac802154.h b/net/mac802154/mac802154.h new file mode 100644 index 000000000000..789d9c948aec --- /dev/null +++ b/net/mac802154/mac802154.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 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: + * Pavel Smolenskiy <pavel.smolenskiy@gmail.com> + * Maxim Gorbachyov <maxim.gorbachev@siemens.com> + * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> + * Alexander Smirnov <alex.bluesman.smirnov@gmail.com> + */ +#ifndef MAC802154_H +#define MAC802154_H + +/* mac802154 device private data */ +struct mac802154_priv { + struct ieee802154_dev hw; + struct ieee802154_ops *ops; + + /* ieee802154 phy */ + struct wpan_phy *phy; + + int open_count; + + /* As in mac80211 slaves list is modified: + * 1) under the RTNL + * 2) protected by slaves_mtx; + * 3) in an RCU manner + * + * So atomic readers can use any of this protection methods. + */ + struct list_head slaves; + struct mutex slaves_mtx; + + /* This one is used for scanning and other jobs not to be interfered + * with serial driver. + */ + struct workqueue_struct *dev_workqueue; + + /* SoftMAC device is registered and running. One can add subinterfaces. + * This flag should be modified under slaves_mtx and RTNL, so you can + * read them using any of protection methods. + */ + bool running; +}; + +#define MAC802154_DEVICE_STOPPED 0x00 +#define MAC802154_DEVICE_RUN 0x01 + +/* Slave interface definition. + * + * Slaves represent typical network interfaces available from userspace. + * Each ieee802154 device/transceiver may have several slaves and able + * to be associated with several networks at the same time. + */ +struct mac802154_sub_if_data { + struct list_head list; /* the ieee802154_priv->slaves list */ + + struct mac802154_priv *hw; + struct net_device *dev; + + int type; + + spinlock_t mib_lock; + + __le16 pan_id; + __le16 short_addr; + + u8 chan; + u8 page; + + /* MAC BSN field */ + u8 bsn; + /* MAC DSN field */ + u8 dsn; +}; + +#define mac802154_to_priv(_hw) container_of(_hw, struct mac802154_priv, hw) + +#define MAC802154_MAX_XMIT_ATTEMPTS 3 + +#define MAC802154_CHAN_NONE (~(u8)0) /* No channel is assigned */ + +extern struct ieee802154_reduced_mlme_ops mac802154_mlme_reduced; + +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); + +netdev_tx_t mac802154_tx(struct mac802154_priv *priv, struct sk_buff *skb, + u8 page, u8 chan); + +/* MIB callbacks */ +void mac802154_dev_set_ieee_addr(struct net_device *dev); + +#endif /* MAC802154_H */ diff --git a/net/mac802154/mac_cmd.c b/net/mac802154/mac_cmd.c new file mode 100644 index 000000000000..7a5d0e052cd7 --- /dev/null +++ b/net/mac802154/mac_cmd.c @@ -0,0 +1,45 @@ +/* + * MAC commands interface + * + * 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: + * Sergey Lapin <slapin@ossfans.org> + * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> + * Alexander Smirnov <alex.bluesman.smirnov@gmail.com> + */ + +#include <linux/skbuff.h> +#include <linux/if_arp.h> + +#include <net/ieee802154_netdev.h> +#include <net/wpan-phy.h> +#include <net/mac802154.h> + +#include "mac802154.h" + +struct wpan_phy *mac802154_get_phy(const struct net_device *dev) +{ + struct mac802154_sub_if_data *priv = netdev_priv(dev); + + BUG_ON(dev->type != ARPHRD_IEEE802154); + + return to_phy(get_device(&priv->hw->phy->dev)); +} + +struct ieee802154_reduced_mlme_ops mac802154_mlme_reduced = { + .get_phy = mac802154_get_phy, +}; diff --git a/net/mac802154/mib.c b/net/mac802154/mib.c new file mode 100644 index 000000000000..ab59821ec729 --- /dev/null +++ b/net/mac802154/mib.c @@ -0,0 +1,93 @@ +/* + * 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/if_arp.h> + +#include <net/mac802154.h> +#include <net/wpan-phy.h> + +#include "mac802154.h" + +struct hw_addr_filt_notify_work { + struct work_struct work; + struct net_device *dev; + unsigned long changed; +}; + +struct mac802154_priv *mac802154_slave_get_priv(struct net_device *dev) +{ + struct mac802154_sub_if_data *priv = netdev_priv(dev); + + BUG_ON(dev->type != ARPHRD_IEEE802154); + + return priv->hw; +} + +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, work); + struct mac802154_priv *hw = mac802154_slave_get_priv(nw->dev); + int res; + + res = hw->ops->set_hw_addr_filt(&hw->hw, + &hw->hw.hw_filt, + nw->changed); + if (res) + pr_debug("failed changed mask %lx\n", nw->changed); + + kfree(nw); + + return; +} + +static void set_hw_addr_filt(struct net_device *dev, unsigned long changed) +{ + struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct hw_addr_filt_notify_work *work; + + work = kzalloc(sizeof(*work), GFP_ATOMIC); + if (!work) + return; + + INIT_WORK(&work->work, hw_addr_notify); + work->dev = dev; + work->changed = changed; + queue_work(priv->hw->dev_workqueue, &work->work); + + return; +} + +void mac802154_dev_set_ieee_addr(struct net_device *dev) +{ + struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct mac802154_priv *mac = priv->hw; + + if (mac->ops->set_hw_addr_filt && + memcmp(mac->hw.hw_filt.ieee_addr, + dev->dev_addr, IEEE802154_ADDR_LEN)) { + memcpy(mac->hw.hw_filt.ieee_addr, + dev->dev_addr, IEEE802154_ADDR_LEN); + set_hw_addr_filt(dev, IEEE802515_AFILT_IEEEADDR_CHANGED); + } +} diff --git a/net/mac802154/monitor.c b/net/mac802154/monitor.c new file mode 100644 index 000000000000..434a26f76a80 --- /dev/null +++ b/net/mac802154/monitor.c @@ -0,0 +1,116 @@ +/* + * 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) + 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; +} diff --git a/net/mac802154/rx.c b/net/mac802154/rx.c new file mode 100644 index 000000000000..4a7d76d4f8bc --- /dev/null +++ b/net/mac802154/rx.c @@ -0,0 +1,114 @@ +/* + * Copyright (C) 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: + * Pavel Smolenskiy <pavel.smolenskiy@gmail.com> + * Maxim Gorbachyov <maxim.gorbachev@siemens.com> + * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> + * Alexander Smirnov <alex.bluesman.smirnov@gmail.com> + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/workqueue.h> +#include <linux/netdevice.h> +#include <linux/crc-ccitt.h> + +#include <net/mac802154.h> +#include <net/ieee802154_netdev.h> + +#include "mac802154.h" + +/* The IEEE 802.15.4 standard defines 4 MAC packet types: + * - 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); + + mac_cb(skb)->lqi = lqi; + skb->protocol = htons(ETH_P_IEEE802154); + skb_reset_mac_header(skb); + + BUILD_BUG_ON(sizeof(struct ieee802154_mac_cb) > sizeof(skb->cb)); + + if (!(priv->hw.flags & IEEE802154_HW_OMIT_CKSUM)) { + u16 crc; + + if (skb->len < 2) { + pr_debug("got invalid frame\n"); + goto out; + } + crc = crc_ccitt(0, skb->data, skb->len); + if (crc) { + pr_debug("CRC mismatch\n"); + goto out; + } + skb_trim(skb, skb->len - 2); /* CRC */ + } + + mac802154_monitors_rx(priv, skb); +out: + dev_kfree_skb(skb); + return; +} + +static void mac802154_rx_worker(struct work_struct *work) +{ + struct rx_work *rw = container_of(work, struct rx_work, work); + struct sk_buff *skb = rw->skb; + + mac802154_subif_rx(rw->dev, skb, rw->lqi); + kfree(rw); +} + +void +ieee802154_rx_irqsafe(struct ieee802154_dev *dev, struct sk_buff *skb, u8 lqi) +{ + struct mac802154_priv *priv = mac802154_to_priv(dev); + struct rx_work *work; + + if (!skb) + return; + + work = kzalloc(sizeof(struct rx_work), GFP_ATOMIC); + 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); diff --git a/net/mac802154/tx.c b/net/mac802154/tx.c new file mode 100644 index 000000000000..8781d8f904d9 --- /dev/null +++ b/net/mac802154/tx.c @@ -0,0 +1,116 @@ +/* + * 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/if_arp.h> +#include <linux/crc-ccitt.h> + +#include <net/mac802154.h> +#include <net/wpan-phy.h> + +#include "mac802154.h" + +/* IEEE 802.15.4 transceivers can sleep during the xmit session, so process + * packets through the workqueue. + */ +struct xmit_work { + struct sk_buff *skb; + struct work_struct work; + struct mac802154_priv *priv; + u8 chan; + u8 page; + u8 xmit_attempts; +}; + +static void mac802154_xmit_worker(struct work_struct *work) +{ + struct xmit_work *xw = container_of(work, struct xmit_work, work); + int res; + + mutex_lock(&xw->priv->phy->pib_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; + } + } + + res = xw->priv->ops->xmit(&xw->priv->hw, xw->skb); + +out: + mutex_unlock(&xw->priv->phy->pib_lock); + + if (res) { + if (xw->xmit_attempts++ < MAC802154_MAX_XMIT_ATTEMPTS) { + queue_work(xw->priv->dev_workqueue, &xw->work); + return; + } else + pr_debug("transmission failed for %d times", + MAC802154_MAX_XMIT_ATTEMPTS); + } + + 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; + + if (!(priv->phy->channels_supported[page] & (1 << chan))) + WARN_ON(1); + return NETDEV_TX_OK; + + 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)) { + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } + + work = kzalloc(sizeof(struct xmit_work), GFP_ATOMIC); + if (!work) + return NETDEV_TX_BUSY; + + INIT_WORK(&work->work, mac802154_xmit_worker); + work->skb = skb; + work->priv = priv; + work->page = page; + work->chan = chan; + work->xmit_attempts = 0; + + queue_work(priv->dev_workqueue, &work->work); + + return NETDEV_TX_OK; +} |