#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/crc-ccitt.h>
+#include <asm/unaligned.h>
+#include <net/rtnetlink.h>
#include <net/ieee802154_netdev.h>
#include <net/mac802154.h>
#include <net/cfg802154.h>
#include "ieee802154_i.h"
+#include "driver-ops.h"
/* IEEE 802.15.4 transceivers can sleep during the xmit session, so process
* packets through the workqueue.
*/
-struct wpan_xmit_cb {
+struct ieee802154_xmit_cb {
struct sk_buff *skb;
struct work_struct work;
struct ieee802154_local *local;
};
-static inline struct wpan_xmit_cb *wpan_xmit_cb(const struct sk_buff *skb)
-{
- BUILD_BUG_ON(sizeof(skb->cb) < sizeof(struct wpan_xmit_cb));
-
- return (struct wpan_xmit_cb *)skb->cb;
-}
+static struct ieee802154_xmit_cb ieee802154_xmit_cb;
-static void mac802154_xmit_worker(struct work_struct *work)
+static void ieee802154_xmit_worker(struct work_struct *work)
{
- struct wpan_xmit_cb *cb = container_of(work, struct wpan_xmit_cb, work);
+ struct ieee802154_xmit_cb *cb =
+ 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;
- res = local->ops->xmit(&local->hw, skb);
- if (res) {
- pr_debug("transmission failed\n");
- /* Restart the netif queue on each sub_if_data object. */
- ieee802154_wake_queue(&local->hw);
- kfree_skb(skb);
- } else {
- /* Restart the netif queue on each sub_if_data object. */
- ieee802154_xmit_complete(&local->hw, skb);
- }
+ rtnl_lock();
+
+ /* check if ifdown occurred while schedule */
+ if (!netif_running(dev))
+ goto err_tx;
+
+ res = drv_xmit_sync(local, skb);
+ if (res)
+ goto err_tx;
+
+ ieee802154_xmit_complete(&local->hw, skb, false);
+
+ 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. */
+ ieee802154_wake_queue(&local->hw);
+ rtnl_unlock();
+ kfree_skb(skb);
+ netdev_dbg(dev, "transmission failed\n");
}
static netdev_tx_t
-mac802154_tx(struct ieee802154_local *local, struct sk_buff *skb)
+ieee802154_tx(struct ieee802154_local *local, struct sk_buff *skb)
{
- struct wpan_xmit_cb *cb = wpan_xmit_cb(skb);
-
- mac802154_monitors_rx(local, skb);
+ struct net_device *dev = skb->dev;
+ int ret;
- if (!(local->hw.flags & IEEE802154_HW_OMIT_CKSUM)) {
+ if (!(local->hw.flags & IEEE802154_HW_TX_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;
+ put_unaligned_le16(crc, skb_put(skb, 2));
}
if (skb_cow_head(skb, local->hw.extra_tx_headroom))
/* Stop the netif queue on each sub_if_data object. */
ieee802154_stop_queue(&local->hw);
- INIT_WORK(&cb->work, mac802154_xmit_worker);
- cb->skb = skb;
- cb->local = local;
+ /* async is priority, otherwise sync is fallback */
+ if (local->ops->xmit_async) {
+ ret = drv_xmit_async(local, skb);
+ if (ret) {
+ ieee802154_wake_queue(&local->hw);
+ goto err_tx;
+ }
- queue_work(local->workqueue, &cb->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;
}
-netdev_tx_t mac802154_monitor_xmit(struct sk_buff *skb, struct net_device *dev)
+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;
- dev->stats.tx_packets++;
- dev->stats.tx_bytes += skb->len;
- return mac802154_tx(sdata->local, skb);
+ return ieee802154_tx(sdata->local, skb);
}
-netdev_tx_t mac802154_wpan_xmit(struct sk_buff *skb, struct net_device *dev)
+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) {
- pr_warn("encryption failed: %i\n", rc);
+ netdev_warn(dev, "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(sdata->local, skb);
+ return ieee802154_tx(sdata->local, skb);
}