diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c index b0d68d7061c1d7c3174200dc76ad933a9662d041..06a3e9013d60578b77e957d60ae263607b4b1af9 100644 --- a/drivers/net/ieee802154/at86rf230.c +++ b/drivers/net/ieee802154/at86rf230.c @@ -1237,7 +1237,7 @@ at86rf230_set_frame_retries(struct ieee802154_hw *hw, s8 retries) static struct ieee802154_ops at86rf230_ops = { .owner = THIS_MODULE, - .xmit = at86rf230_xmit, + .xmit_sync = at86rf230_xmit, .ed = at86rf230_ed, .set_channel = at86rf230_channel, .start = at86rf230_start, diff --git a/drivers/net/ieee802154/cc2520.c b/drivers/net/ieee802154/cc2520.c index b827e04d481bf656f1f695fd992ba374abf19c42..f6f07f4eb0ec4e76d88fab89bac64bcc9fe51bac 100644 --- a/drivers/net/ieee802154/cc2520.c +++ b/drivers/net/ieee802154/cc2520.c @@ -635,7 +635,7 @@ static struct ieee802154_ops cc2520_ops = { .owner = THIS_MODULE, .start = cc2520_start, .stop = cc2520_stop, - .xmit = cc2520_tx, + .xmit_sync = cc2520_tx, .ed = cc2520_ed, .set_channel = cc2520_set_channel, .set_hw_addr_filt = cc2520_filter, diff --git a/drivers/net/ieee802154/fakelb.c b/drivers/net/ieee802154/fakelb.c index 51e3c589d2e43b2d7ac2a3cf57ea70411435b4b2..db0703f0fa4113dfbf93ca3701622f61ef8670e5 100644 --- a/drivers/net/ieee802154/fakelb.c +++ b/drivers/net/ieee802154/fakelb.c @@ -131,7 +131,7 @@ fakelb_hw_stop(struct ieee802154_hw *hw) { static struct ieee802154_ops fakelb_ops = { .owner = THIS_MODULE, - .xmit = fakelb_hw_xmit, + .xmit_sync = fakelb_hw_xmit, .ed = fakelb_hw_ed, .set_channel = fakelb_hw_channel, .start = fakelb_hw_start, diff --git a/drivers/net/ieee802154/mrf24j40.c b/drivers/net/ieee802154/mrf24j40.c index 2e267c5c44da3645b954f0c45c084b14aa0afcb2..3d775afa217fe2c21373f547c251b12dbfe85c55 100644 --- a/drivers/net/ieee802154/mrf24j40.c +++ b/drivers/net/ieee802154/mrf24j40.c @@ -581,7 +581,7 @@ static int mrf24j40_handle_rx(struct mrf24j40 *devrec) static struct ieee802154_ops mrf24j40_ops = { .owner = THIS_MODULE, - .xmit = mrf24j40_tx, + .xmit_sync = mrf24j40_tx, .ed = mrf24j40_ed, .start = mrf24j40_start, .stop = mrf24j40_stop, diff --git a/include/net/mac802154.h b/include/net/mac802154.h index 29af5c346ebfbcfebabb133fd5eaf3b25b944ad1..57b120281afc13062fdadaf11c4ca9c775c92ab1 100644 --- a/include/net/mac802154.h +++ b/include/net/mac802154.h @@ -109,7 +109,16 @@ struct ieee802154_hw { * stop: Handler that 802.15.4 module calls for device cleanup. * 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. + * + * 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. * The low-level driver should send the frame based on available * configuration. @@ -160,8 +169,10 @@ struct ieee802154_ops { struct module *owner; int (*start)(struct ieee802154_hw *hw); void (*stop)(struct ieee802154_hw *hw); - int (*xmit)(struct ieee802154_hw *hw, - struct sk_buff *skb); + int (*xmit_sync)(struct ieee802154_hw *hw, + struct sk_buff *skb); + int (*xmit_async)(struct ieee802154_hw *hw, + struct sk_buff *skb); int (*ed)(struct ieee802154_hw *hw, u8 *level); int (*set_channel)(struct ieee802154_hw *hw, int page, diff --git a/net/mac802154/main.c b/net/mac802154/main.c index 0e9a6a203f7a96a66878724634b344eae1d142fd..3c0a824d24ac6fa7cedbc900a5ecad46d33b1e6b 100644 --- a/net/mac802154/main.c +++ b/net/mac802154/main.c @@ -229,8 +229,8 @@ ieee802154_alloc_hw(size_t priv_data_len, struct ieee802154_ops *ops) struct ieee802154_local *local; size_t priv_size; - if (!ops || !ops->xmit || !ops->ed || !ops->start || - !ops->stop || !ops->set_channel) { + 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; } diff --git a/net/mac802154/tx.c b/net/mac802154/tx.c index 23139cae076499d858e74f2c60fbb9f46af53318..1a4f6d91ab8c2ee8b248f16b23f6ea9b74751fb1 100644 --- a/net/mac802154/tx.c +++ b/net/mac802154/tx.c @@ -50,7 +50,7 @@ static void mac802154_xmit_worker(struct work_struct *work) struct sk_buff *skb = cb->skb; int res; - res = local->ops->xmit(&local->hw, skb); + res = local->ops->xmit_sync(&local->hw, skb); if (res) { pr_debug("transmission failed\n"); /* Restart the netif queue on each sub_if_data object. */ @@ -66,6 +66,7 @@ static netdev_tx_t mac802154_tx(struct ieee802154_local *local, struct sk_buff *skb) { struct wpan_xmit_cb *cb = wpan_xmit_cb(skb); + int ret; mac802154_monitors_rx(local, skb); @@ -83,11 +84,20 @@ mac802154_tx(struct ieee802154_local *local, struct sk_buff *skb) /* 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 = local->ops->xmit_async(&local->hw, skb); + if (ret) { + ieee802154_wake_queue(&local->hw); + goto err_tx; + } + } else { + INIT_WORK(&cb->work, mac802154_xmit_worker); + cb->skb = skb; + cb->local = local; - queue_work(local->workqueue, &cb->work); + queue_work(local->workqueue, &cb->work); + } return NETDEV_TX_OK;