/* mac802154_scan_cleanup_locked() must be called upon scan completion or abort. * - Completions are asynchronous, not locked by the rtnl and decided by the * scan worker. * - Aborts are decided by userspace, and locked by the rtnl. * * Concurrent modifications to the PHY, the interfaces or the hardware is in * general prevented by the rtnl. So in most cases we don't need additional * protection. * * However, the scan worker get's triggered without anybody noticing and thus we * must ensure the presence of the devices as well as data consistency: * - The sub-interface and device driver module get both their reference * counters incremented whenever we start a scan, so they cannot disappear * during operation. * - Data consistency is achieved by the use of rcu protected pointers.
*/ staticint mac802154_scan_cleanup_locked(struct ieee802154_local *local, struct ieee802154_sub_if_data *sdata, bool aborted)
{ struct wpan_dev *wpan_dev = &sdata->wpan_dev; struct wpan_phy *wpan_phy = local->phy; struct cfg802154_scan_request *request;
u8 arg;
/* Prevent any further use of the scan request */
clear_bit(IEEE802154_IS_SCANNING, &local->ongoing);
cancel_delayed_work(&local->scan_work);
request = rcu_replace_pointer(local->scan_req, NULL, 1); if (!request) return 0;
kvfree_rcu_mightsleep(request);
/* Advertize first, while we know the devices cannot be removed */ if (aborted)
arg = NL802154_SCAN_DONE_REASON_ABORTED; else
arg = NL802154_SCAN_DONE_REASON_FINISHED;
nl802154_scan_done(wpan_phy, wpan_dev, arg);
/* Set the hardware back in its original state */
drv_set_channel(local, wpan_phy->current_page,
wpan_phy->current_channel);
ieee802154_configure_durations(wpan_phy, wpan_phy->current_page,
wpan_phy->current_channel);
drv_stop(local);
synchronize_net();
sdata->required_filtering = sdata->iface_default_filtering;
drv_start(local, sdata->required_filtering, &local->addr_filt);
return 0;
}
int mac802154_abort_scan_locked(struct ieee802154_local *local, struct ieee802154_sub_if_data *sdata)
{
ASSERT_RTNL();
/* Ensure the device receiver is turned off when changing channels * because there is no atomic way to change the channel and know on * which one a beacon might have been received.
*/
drv_stop(local);
synchronize_net();
mac802154_flush_queued_beacons(local);
rcu_read_lock();
scan_req = rcu_dereference(local->scan_req); if (unlikely(!scan_req)) {
rcu_read_unlock(); return;
}
/* Wait an arbitrary amount of time in case we cannot use the device */ if (local->suspended || !ieee802154_sdata_running(sdata)) {
rcu_read_unlock();
queue_delayed_work(local->mac_wq, &local->scan_work,
msecs_to_jiffies(1000)); return;
}
/* Look for the next valid chan */
page = local->scan_page;
channel = local->scan_channel; do {
ret = mac802154_scan_find_next_chan(local, scan_req, page, &channel); if (ret) {
rcu_read_unlock(); goto end_scan;
}
} while (!ieee802154_chan_is_valid(scan_req->wpan_phy, page, channel));
rcu_read_unlock();
/* Bypass the stack on purpose when changing the channel */
rtnl_lock();
ret = drv_set_channel(local, page, channel);
rtnl_unlock(); if (ret) {
dev_err(&sdata->dev->dev, "Channel change failure during scan, aborting (%d)\n", ret); goto end_scan;
}
rtnl_lock();
ret = drv_start(local, IEEE802154_FILTERING_3_SCAN, &local->addr_filt);
rtnl_unlock(); if (ret) {
dev_err(&sdata->dev->dev, "Restarting failure after channel change, aborting (%d)\n", ret); goto end_scan;
}
if (scan_req_type == NL802154_SCAN_ACTIVE) {
ret = mac802154_transmit_beacon_req(local, sdata); if (ret)
dev_err(&sdata->dev->dev, "Error when transmitting beacon request (%d)\n", ret);
}
if (request->type != NL802154_SCAN_PASSIVE &&
request->type != NL802154_SCAN_ACTIVE) return -EOPNOTSUPP;
/* Store scanning parameters */
rcu_assign_pointer(local->scan_req, request);
/* Software scanning requires to set promiscuous mode, so we need to * pause the Tx queue during the entire operation.
*/
ieee802154_mlme_op_pre(local);
ret = ieee802154_beacon_push(skb, &local->beacon); if (ret) {
kfree_skb(skb); return ret;
}
/* Using the MLME transmission helper for sending beacons is a bit * overkill because we do not really care about the final outcome. * * Even though, going through the whole net stack with a regular * dev_queue_xmit() is not relevant either because we want beacons to be * sent "now" rather than go through the whole net stack scheduling * (qdisc & co). * * Finally, using ieee802154_subif_start_xmit() would only be an option * if we had a generic transmit helper which would acquire the * HARD_TX_LOCK() to prevent buffer handling conflicts with regular * packets. * * So for now we keep it simple and send beacons with our MLME helper, * even if it stops the ieee802154 queue entirely during these * transmissions, wich anyway does not have a huge impact on the * performances given the current design of the stack.
*/ return ieee802154_mlme_tx(local, sdata, skb);
}
/* Wait an arbitrary amount of time in case we cannot use the device */ if (local->suspended || !ieee802154_sdata_running(sdata)) {
rcu_read_unlock();
queue_delayed_work(local->mac_wq, &local->beacon_work,
msecs_to_jiffies(1000)); return;
}
dev_dbg(&sdata->dev->dev, "Sending beacon\n");
ret = mac802154_transmit_beacon(local, wpan_dev); if (ret)
dev_err(&sdata->dev->dev, "Beacon could not be transmitted (%d)\n", ret);
if (interval < IEEE802154_ACTIVE_SCAN_DURATION)
queue_delayed_work(local->mac_wq, &local->beacon_work,
local->beacon_interval);
}
ret = ieee802154_mlme_tx_one_locked(local, sdata, skb); if (ret) { if (ret > 0)
ret = (ret == IEEE802154_NO_ACK) ? -EREMOTEIO : -EIO;
dev_warn(&sdata->dev->dev, "No ASSOC REQ ACK received from %8phC\n", &ceaddr); goto clear_assoc;
}
ret = wait_for_completion_killable_timeout(&local->assoc_done, 10 * HZ); if (ret <= 0) {
dev_warn(&sdata->dev->dev, "No ASSOC RESP received from %8phC\n", &ceaddr);
ret = -ETIMEDOUT; goto clear_assoc;
}
if (local->assoc_status != IEEE802154_ASSOCIATION_SUCCESSFUL) { if (local->assoc_status == IEEE802154_PAN_AT_CAPACITY)
ret = -ERANGE; else
ret = -EPERM;
dev_warn(&sdata->dev->dev, "Negative ASSOC RESP received from %8phC: %s\n", &ceaddr,
local->assoc_status == IEEE802154_PAN_AT_CAPACITY ? "PAN at capacity" : "access denied");
}
skb = alloc_skb(IEEE802154_MAC_CMD_SKB_SZ + sizeof(frame.disassoc_pl),
GFP_KERNEL); if (!skb) return -ENOBUFS;
skb->dev = sdata->dev;
ret = ieee802154_mac_cmd_push(skb, &frame, &frame.disassoc_pl, sizeof(frame.disassoc_pl)); if (ret) {
kfree_skb(skb); return ret;
}
ret = ieee802154_mlme_tx_one_locked(local, sdata, skb); if (ret) {
dev_warn(&sdata->dev->dev, "No DISASSOC ACK received from %8phC\n", &teaddr); if (ret > 0)
ret = (ret == IEEE802154_NO_ACK) ? -EREMOTEIO : -EIO; return ret;
}
dev_dbg(&sdata->dev->dev, "DISASSOC ACK received from %8phC\n", &teaddr); return 0;
}
skb = alloc_skb(IEEE802154_MAC_CMD_SKB_SZ + sizeof(*assoc_resp_pl),
GFP_KERNEL); if (!skb) return -ENOBUFS;
skb->dev = sdata->dev;
ret = ieee802154_mac_cmd_push(skb, &frame, assoc_resp_pl, sizeof(*assoc_resp_pl)); if (ret) {
kfree_skb(skb); return ret;
}
ret = ieee802154_mlme_tx_locked(local, sdata, skb); if (ret) {
dev_warn(&sdata->dev->dev, "No ASSOC RESP ACK received from %8phC\n", &teaddr); if (ret > 0)
ret = (ret == IEEE802154_NO_ACK) ? -EREMOTEIO : -EIO; return ret;
}
ret = mac802154_send_association_resp_locked(sdata, child, &assoc_resp_pl); if (ret || assoc_resp_pl.status != IEEE802154_ASSOCIATION_SUCCESSFUL) {
kfree(child); goto unlock;
}
dev_dbg(&sdata->dev->dev, "Successful association with new child %8phC\n", &ceaddr);
/* Ensure this child is not already associated (might happen due to * retransmissions), in this case drop the ex structure.
*/
tmp.mode = child->mode;
tmp.extended_addr = child->extended_addr;
exchild = cfg802154_device_is_child(wpan_dev, &tmp); if (exchild) {
dev_dbg(&sdata->dev->dev, "Child %8phC was already known\n", &ceaddr);
list_del(&exchild->node);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.