mac802154: tx: squash multiple dereferencing
[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 wpan_xmit_cb {
34         struct sk_buff *skb;
35         struct work_struct work;
36         struct ieee802154_local *local;
37         u8 chan;
38         u8 page;
39 };
40
41 static inline struct wpan_xmit_cb *wpan_xmit_cb(const struct sk_buff *skb)
42 {
43         BUILD_BUG_ON(sizeof(skb->cb) < sizeof(struct wpan_xmit_cb));
44
45         return (struct wpan_xmit_cb *)skb->cb;
46 }
47
48 static void mac802154_xmit_worker(struct work_struct *work)
49 {
50         struct wpan_xmit_cb *cb = container_of(work, struct wpan_xmit_cb, work);
51         struct ieee802154_local *local = cb->local;
52         struct ieee802154_sub_if_data *sdata;
53         struct sk_buff *skb = cb->skb;
54         int res;
55
56         mutex_lock(&local->phy->pib_lock);
57         if (local->phy->current_channel != cb->chan ||
58             local->phy->current_page != cb->page) {
59                 res = local->ops->set_channel(&local->hw, cb->page, cb->chan);
60                 if (res) {
61                         pr_debug("set_channel failed\n");
62                         goto out;
63                 }
64
65                 local->phy->current_channel = cb->chan;
66                 local->phy->current_page = cb->page;
67         }
68
69         res = local->ops->xmit(&local->hw, skb);
70         if (res)
71                 pr_debug("transmission failed\n");
72
73 out:
74         mutex_unlock(&local->phy->pib_lock);
75
76         /* Restart the netif queue on each sub_if_data object. */
77         rcu_read_lock();
78         list_for_each_entry_rcu(sdata, &local->interfaces, list)
79                 netif_wake_queue(sdata->dev);
80         rcu_read_unlock();
81
82         dev_kfree_skb(skb);
83 }
84
85 static netdev_tx_t mac802154_tx(struct ieee802154_local *local,
86                                 struct sk_buff *skb, u8 page, u8 chan)
87 {
88         struct ieee802154_sub_if_data *sdata;
89         struct wpan_xmit_cb *cb = wpan_xmit_cb(skb);
90
91         if (!(local->phy->channels_supported[page] & (1 << chan))) {
92                 WARN_ON(1);
93                 goto err_tx;
94         }
95
96         mac802154_monitors_rx(local, skb);
97
98         if (!(local->hw.flags & IEEE802154_HW_OMIT_CKSUM)) {
99                 u16 crc = crc_ccitt(0, skb->data, skb->len);
100                 u8 *data = skb_put(skb, 2);
101
102                 data[0] = crc & 0xff;
103                 data[1] = crc >> 8;
104         }
105
106         if (skb_cow_head(skb, local->hw.extra_tx_headroom))
107                 goto err_tx;
108
109         /* Stop the netif queue on each sub_if_data object. */
110         rcu_read_lock();
111         list_for_each_entry_rcu(sdata, &local->interfaces, list)
112                 netif_stop_queue(sdata->dev);
113         rcu_read_unlock();
114
115         INIT_WORK(&cb->work, mac802154_xmit_worker);
116         cb->skb = skb;
117         cb->local = local;
118         cb->page = page;
119         cb->chan = chan;
120
121         queue_work(local->workqueue, &cb->work);
122
123         return NETDEV_TX_OK;
124
125 err_tx:
126         kfree_skb(skb);
127         return NETDEV_TX_OK;
128 }
129
130 netdev_tx_t mac802154_monitor_xmit(struct sk_buff *skb, struct net_device *dev)
131 {
132         struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
133         u8 chan, page;
134
135         /* FIXME: locking */
136         chan = sdata->local->phy->current_channel;
137         page = sdata->local->phy->current_page;
138
139         if (chan == MAC802154_CHAN_NONE) /* not initialized */
140                 return NETDEV_TX_OK;
141
142         if (WARN_ON(page >= WPAN_NUM_PAGES) ||
143             WARN_ON(chan >= WPAN_NUM_CHANNELS))
144                 return NETDEV_TX_OK;
145
146         skb->skb_iif = dev->ifindex;
147         dev->stats.tx_packets++;
148         dev->stats.tx_bytes += skb->len;
149
150         return mac802154_tx(sdata->local, skb, page, chan);
151 }
152
153 netdev_tx_t mac802154_wpan_xmit(struct sk_buff *skb, struct net_device *dev)
154 {
155         struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
156         u8 chan, page;
157         int rc;
158
159         spin_lock_bh(&sdata->mib_lock);
160         chan = sdata->chan;
161         page = sdata->page;
162         spin_unlock_bh(&sdata->mib_lock);
163
164         if (chan == MAC802154_CHAN_NONE ||
165             page >= WPAN_NUM_PAGES ||
166             chan >= WPAN_NUM_CHANNELS) {
167                 kfree_skb(skb);
168                 return NETDEV_TX_OK;
169         }
170
171         rc = mac802154_llsec_encrypt(&sdata->sec, skb);
172         if (rc) {
173                 pr_warn("encryption failed: %i\n", rc);
174                 kfree_skb(skb);
175                 return NETDEV_TX_OK;
176         }
177
178         skb->skb_iif = dev->ifindex;
179         dev->stats.tx_packets++;
180         dev->stats.tx_bytes += skb->len;
181
182         return mac802154_tx(sdata->local, skb, page, chan);
183 }