8f537bf731cad61928cf7006950903578618db67
[cascardo/linux.git] / net / mac802154 / tx.c
1 /*
2  * Copyright 2007-2012 Siemens AG
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2
6  * as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  * Written by:
14  * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
15  * Sergey Lapin <slapin@ossfans.org>
16  * Maxim Gorbachyov <maxim.gorbachev@siemens.com>
17  * Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
18  */
19
20 #include <linux/netdevice.h>
21 #include <linux/if_arp.h>
22 #include <linux/crc-ccitt.h>
23
24 #include <net/ieee802154_netdev.h>
25 #include <net/mac802154.h>
26 #include <net/cfg802154.h>
27
28 #include "ieee802154_i.h"
29
30 /* IEEE 802.15.4 transceivers can sleep during the xmit session, so process
31  * packets through the workqueue.
32  */
33 struct xmit_work {
34         struct sk_buff *skb;
35         struct work_struct work;
36         struct mac802154_priv *priv;
37         u8 chan;
38         u8 page;
39 };
40
41 static void mac802154_xmit_worker(struct work_struct *work)
42 {
43         struct xmit_work *xw = container_of(work, struct xmit_work, work);
44         struct mac802154_sub_if_data *sdata;
45         int res;
46
47         mutex_lock(&xw->priv->phy->pib_lock);
48         if (xw->priv->phy->current_channel != xw->chan ||
49             xw->priv->phy->current_page != xw->page) {
50                 res = xw->priv->ops->set_channel(&xw->priv->hw,
51                                                   xw->page,
52                                                   xw->chan);
53                 if (res) {
54                         pr_debug("set_channel failed\n");
55                         goto out;
56                 }
57
58                 xw->priv->phy->current_channel = xw->chan;
59                 xw->priv->phy->current_page = xw->page;
60         }
61
62         res = xw->priv->ops->xmit(&xw->priv->hw, xw->skb);
63         if (res)
64                 pr_debug("transmission failed\n");
65
66 out:
67         mutex_unlock(&xw->priv->phy->pib_lock);
68
69         /* Restart the netif queue on each sub_if_data object. */
70         rcu_read_lock();
71         list_for_each_entry_rcu(sdata, &xw->priv->slaves, list)
72                 netif_wake_queue(sdata->dev);
73         rcu_read_unlock();
74
75         dev_kfree_skb(xw->skb);
76
77         kfree(xw);
78 }
79
80 netdev_tx_t mac802154_tx(struct mac802154_priv *priv, struct sk_buff *skb,
81                          u8 page, u8 chan)
82 {
83         struct xmit_work *work;
84         struct mac802154_sub_if_data *sdata;
85
86         if (!(priv->phy->channels_supported[page] & (1 << chan))) {
87                 WARN_ON(1);
88                 goto err_tx;
89         }
90
91         mac802154_monitors_rx(mac802154_to_priv(&priv->hw), skb);
92
93         if (!(priv->hw.flags & IEEE802154_HW_OMIT_CKSUM)) {
94                 u16 crc = crc_ccitt(0, skb->data, skb->len);
95                 u8 *data = skb_put(skb, 2);
96
97                 data[0] = crc & 0xff;
98                 data[1] = crc >> 8;
99         }
100
101         if (skb_cow_head(skb, priv->hw.extra_tx_headroom))
102                 goto err_tx;
103
104         work = kzalloc(sizeof(*work), GFP_ATOMIC);
105         if (!work) {
106                 kfree_skb(skb);
107                 return NETDEV_TX_BUSY;
108         }
109
110         /* Stop the netif queue on each sub_if_data object. */
111         rcu_read_lock();
112         list_for_each_entry_rcu(sdata, &priv->slaves, list)
113                 netif_stop_queue(sdata->dev);
114         rcu_read_unlock();
115
116         INIT_WORK(&work->work, mac802154_xmit_worker);
117         work->skb = skb;
118         work->priv = priv;
119         work->page = page;
120         work->chan = chan;
121
122         queue_work(priv->dev_workqueue, &work->work);
123
124         return NETDEV_TX_OK;
125
126 err_tx:
127         kfree_skb(skb);
128         return NETDEV_TX_OK;
129 }