mac802154: Handle basic beaconing
authorMiquel Raynal <miquel.raynal@bootlin.com>
Wed, 25 Jan 2023 10:29:23 +0000 (11:29 +0100)
committerStefan Schmidt <stefan@datenfreihafen.org>
Sat, 28 Jan 2023 12:55:10 +0000 (13:55 +0100)
Implement the core hooks in order to provide the softMAC layer support
for sending beacons. Coordinators may be requested to send beacons in a
beacon enabled PAN in order for the other devices around to self
discover the available PANs automatically.

Changing the channels is prohibited while a beacon operation is
ongoing.

The implementation uses a workqueue triggered at a certain interval
depending on the symbol duration for the current channel and the
interval order provided.

Sending beacons in response to a BEACON_REQ frame (ie. answering active
scans) is not yet supported.

This initial patchset has no security support (llsec).

Co-developed-by: David Girault <david.girault@qorvo.com>
Signed-off-by: David Girault <david.girault@qorvo.com>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Acked-by: Alexander Aring <aahringo@redhat.com>
Link: https://lore.kernel.org/r/20230125102923.135465-3-miquel.raynal@bootlin.com
Signed-off-by: Stefan Schmidt <stefan@datenfreihafen.org>
include/net/ieee802154_netdev.h
net/ieee802154/header_ops.c
net/mac802154/cfg.c
net/mac802154/ieee802154_i.h
net/mac802154/iface.c
net/mac802154/llsec.c
net/mac802154/main.c
net/mac802154/scan.c

index 2f2196049a86bb274e94a6c6cb570cf6288b509b..da8a3e648c7aac11c3a1e58f5170c849db0cb242 100644 (file)
@@ -129,6 +129,13 @@ enum ieee802154_frame_version {
        IEEE802154_MULTIPURPOSE_STD = IEEE802154_2003_STD,
 };
 
+enum ieee802154_addressing_mode {
+       IEEE802154_NO_ADDRESSING,
+       IEEE802154_RESERVED,
+       IEEE802154_SHORT_ADDRESSING,
+       IEEE802154_EXTENDED_ADDRESSING,
+};
+
 struct ieee802154_hdr {
        struct ieee802154_hdr_fc fc;
        u8 seq;
@@ -137,6 +144,11 @@ struct ieee802154_hdr {
        struct ieee802154_sechdr sec;
 };
 
+struct ieee802154_beacon_frame {
+       struct ieee802154_hdr mhr;
+       struct ieee802154_beacon_hdr mac_pl;
+};
+
 /* pushes hdr onto the skb. fields of hdr->fc that can be calculated from
  * the contents of hdr will be, and the actual value of those bits in
  * hdr->fc will be ignored. this includes the INTRA_PAN bit and the frame
@@ -162,6 +174,10 @@ int ieee802154_hdr_peek_addrs(const struct sk_buff *skb,
  */
 int ieee802154_hdr_peek(const struct sk_buff *skb, struct ieee802154_hdr *hdr);
 
+/* pushes a beacon frame into an skb */
+int ieee802154_beacon_push(struct sk_buff *skb,
+                          struct ieee802154_beacon_frame *beacon);
+
 int ieee802154_max_payload(const struct ieee802154_hdr *hdr);
 
 static inline int
index af337cf627643ea1a32584397996d4f3fd921a0c..35d384dfe29de6a693e2cf5ba49695657f5f9451 100644 (file)
@@ -120,6 +120,30 @@ ieee802154_hdr_push(struct sk_buff *skb, struct ieee802154_hdr *hdr)
 }
 EXPORT_SYMBOL_GPL(ieee802154_hdr_push);
 
+int ieee802154_beacon_push(struct sk_buff *skb,
+                          struct ieee802154_beacon_frame *beacon)
+{
+       struct ieee802154_beacon_hdr *mac_pl = &beacon->mac_pl;
+       struct ieee802154_hdr *mhr = &beacon->mhr;
+       int ret;
+
+       skb_reserve(skb, sizeof(*mhr));
+       ret = ieee802154_hdr_push(skb, mhr);
+       if (ret < 0)
+               return ret;
+
+       skb_reset_mac_header(skb);
+       skb->mac_len = ret;
+
+       skb_put_data(skb, mac_pl, sizeof(*mac_pl));
+
+       if (mac_pl->pend_short_addr_count || mac_pl->pend_ext_addr_count)
+               return -EOPNOTSUPP;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(ieee802154_beacon_push);
+
 static int
 ieee802154_hdr_get_addr(const u8 *buf, int mode, bool omit_pan,
                        struct ieee802154_addr *addr)
index 187cebcaf23332cc86e5fdacf35b45d26a71f85a..5c3cb019f7513d2fefd4dd016f984fd57f804b38 100644 (file)
@@ -114,8 +114,8 @@ ieee802154_set_channel(struct wpan_phy *wpan_phy, u8 page, u8 channel)
            wpan_phy->current_channel == channel)
                return 0;
 
-       /* Refuse to change channels during a scanning operation */
-       if (mac802154_is_scanning(local))
+       /* Refuse to change channels during scanning or beaconing */
+       if (mac802154_is_scanning(local) || mac802154_is_beaconing(local))
                return -EBUSY;
 
        ret = drv_set_channel(local, page, channel);
@@ -290,6 +290,31 @@ static int mac802154_abort_scan(struct wpan_phy *wpan_phy,
        return mac802154_abort_scan_locked(local, sdata);
 }
 
+static int mac802154_send_beacons(struct wpan_phy *wpan_phy,
+                                 struct cfg802154_beacon_request *request)
+{
+       struct ieee802154_sub_if_data *sdata;
+
+       sdata = IEEE802154_WPAN_DEV_TO_SUB_IF(request->wpan_dev);
+
+       ASSERT_RTNL();
+
+       return mac802154_send_beacons_locked(sdata, request);
+}
+
+static int mac802154_stop_beacons(struct wpan_phy *wpan_phy,
+                                 struct wpan_dev *wpan_dev)
+{
+       struct ieee802154_local *local = wpan_phy_priv(wpan_phy);
+       struct ieee802154_sub_if_data *sdata;
+
+       sdata = IEEE802154_WPAN_DEV_TO_SUB_IF(wpan_dev);
+
+       ASSERT_RTNL();
+
+       return mac802154_stop_beacons_locked(local, sdata);
+}
+
 #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
 static void
 ieee802154_get_llsec_table(struct wpan_phy *wpan_phy,
@@ -499,6 +524,8 @@ const struct cfg802154_ops mac802154_config_ops = {
        .set_ackreq_default = ieee802154_set_ackreq_default,
        .trigger_scan = mac802154_trigger_scan,
        .abort_scan = mac802154_abort_scan,
+       .send_beacons = mac802154_send_beacons,
+       .stop_beacons = mac802154_stop_beacons,
 #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
        .get_llsec_table = ieee802154_get_llsec_table,
        .lock_llsec_table = ieee802154_lock_llsec_table,
index 0e4db967bd1d34364d2cea2704085272ef62b180..63bab99ed368559e22f44a3ecec50e587e81a68b 100644 (file)
@@ -23,6 +23,7 @@
 
 enum ieee802154_ongoing {
        IEEE802154_IS_SCANNING = BIT(0),
+       IEEE802154_IS_BEACONING = BIT(1),
 };
 
 /* mac802154 device private data */
@@ -60,6 +61,12 @@ struct ieee802154_local {
        struct cfg802154_scan_request __rcu *scan_req;
        struct delayed_work scan_work;
 
+       /* Beaconing */
+       unsigned int beacon_interval;
+       struct ieee802154_beacon_frame beacon;
+       struct cfg802154_beacon_request __rcu *beacon_req;
+       struct delayed_work beacon_work;
+
        /* Asynchronous tasks */
        struct list_head rx_beacon_list;
        struct work_struct rx_beacon_work;
@@ -257,6 +264,17 @@ static inline bool mac802154_is_scanning(struct ieee802154_local *local)
        return test_bit(IEEE802154_IS_SCANNING, &local->ongoing);
 }
 
+void mac802154_beacon_worker(struct work_struct *work);
+int mac802154_send_beacons_locked(struct ieee802154_sub_if_data *sdata,
+                                 struct cfg802154_beacon_request *request);
+int mac802154_stop_beacons_locked(struct ieee802154_local *local,
+                                 struct ieee802154_sub_if_data *sdata);
+
+static inline bool mac802154_is_beaconing(struct ieee802154_local *local)
+{
+       return test_bit(IEEE802154_IS_BEACONING, &local->ongoing);
+}
+
 /* interface handling */
 int ieee802154_iface_init(void);
 void ieee802154_iface_exit(void);
index a5958d323ea370b54c0cfb99c54a7b2063c4aaf3..9d59caeb74e0b35a1a5b3cbd8338e0d6c0239d6a 100644 (file)
@@ -305,6 +305,9 @@ static int mac802154_slave_close(struct net_device *dev)
        if (mac802154_is_scanning(local))
                mac802154_abort_scan_locked(local, sdata);
 
+       if (mac802154_is_beaconing(local))
+               mac802154_stop_beacons_locked(local, sdata);
+
        netif_stop_queue(dev);
        local->open_count--;
 
index 55550ead2ced8cbd1273bb4c0a02a0928080565f..8d2eabc71bbeb04358dc04bb40e3ac03188e6380 100644 (file)
@@ -707,7 +707,10 @@ int mac802154_llsec_encrypt(struct mac802154_llsec *sec, struct sk_buff *skb)
 
        hlen = ieee802154_hdr_pull(skb, &hdr);
 
-       if (hlen < 0 || hdr.fc.type != IEEE802154_FC_TYPE_DATA)
+       /* TODO: control frames security support */
+       if (hlen < 0 ||
+           (hdr.fc.type != IEEE802154_FC_TYPE_DATA &&
+            hdr.fc.type != IEEE802154_FC_TYPE_BEACON))
                return -EINVAL;
 
        if (!hdr.fc.security_enabled ||
index b1111279e06d85fe62d2a954111b446859a77fca..ee23e234b998b3e7fff3c1d34f06461511e4cb61 100644 (file)
@@ -99,6 +99,7 @@ ieee802154_alloc_hw(size_t priv_data_len, const struct ieee802154_ops *ops)
        INIT_WORK(&local->sync_tx_work, ieee802154_xmit_sync_worker);
        INIT_DELAYED_WORK(&local->scan_work, mac802154_scan_worker);
        INIT_WORK(&local->rx_beacon_work, mac802154_rx_beacon_worker);
+       INIT_DELAYED_WORK(&local->beacon_work, mac802154_beacon_worker);
 
        /* init supported flags with 802.15.4 default ranges */
        phy->supported.max_minbe = 8;
index 56056b9c93c1504da883d580932e33e6d0ab47ff..cfbe20b1ec5e6a51ae8e975de1f9a318bc95f1bd 100644 (file)
 #include "driver-ops.h"
 #include "../ieee802154/nl802154.h"
 
+#define IEEE802154_BEACON_MHR_SZ 13
+#define IEEE802154_BEACON_PL_SZ 4
+#define IEEE802154_BEACON_SKB_SZ (IEEE802154_BEACON_MHR_SZ + \
+                                 IEEE802154_BEACON_PL_SZ)
+
 /* 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.
@@ -286,3 +291,149 @@ int mac802154_process_beacon(struct ieee802154_local *local,
 
        return 0;
 }
+
+static int mac802154_transmit_beacon(struct ieee802154_local *local,
+                                    struct wpan_dev *wpan_dev)
+{
+       struct cfg802154_beacon_request *beacon_req;
+       struct ieee802154_sub_if_data *sdata;
+       struct sk_buff *skb;
+       int ret;
+
+       /* Update the sequence number */
+       local->beacon.mhr.seq = atomic_inc_return(&wpan_dev->bsn) & 0xFF;
+
+       skb = alloc_skb(IEEE802154_BEACON_SKB_SZ, GFP_KERNEL);
+       if (!skb)
+               return -ENOBUFS;
+
+       rcu_read_lock();
+       beacon_req = rcu_dereference(local->beacon_req);
+       if (unlikely(!beacon_req)) {
+               rcu_read_unlock();
+               kfree_skb(skb);
+               return -EINVAL;
+       }
+
+       sdata = IEEE802154_WPAN_DEV_TO_SUB_IF(beacon_req->wpan_dev);
+       skb->dev = sdata->dev;
+
+       rcu_read_unlock();
+
+       ret = ieee802154_beacon_push(skb, &local->beacon);
+       if (ret) {
+               kfree_skb(skb);
+               return ret;
+       }
+
+       return ieee802154_subif_start_xmit(skb, sdata->dev);
+}
+
+void mac802154_beacon_worker(struct work_struct *work)
+{
+       struct ieee802154_local *local =
+               container_of(work, struct ieee802154_local, beacon_work.work);
+       struct cfg802154_beacon_request *beacon_req;
+       struct ieee802154_sub_if_data *sdata;
+       struct wpan_dev *wpan_dev;
+       int ret;
+
+       rcu_read_lock();
+       beacon_req = rcu_dereference(local->beacon_req);
+       if (unlikely(!beacon_req)) {
+               rcu_read_unlock();
+               return;
+       }
+
+       sdata = IEEE802154_WPAN_DEV_TO_SUB_IF(beacon_req->wpan_dev);
+
+       /* 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;
+       }
+
+       wpan_dev = beacon_req->wpan_dev;
+
+       rcu_read_unlock();
+
+       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 (local->beacon_interval >= 0)
+               queue_delayed_work(local->mac_wq, &local->beacon_work,
+                                  local->beacon_interval);
+}
+
+int mac802154_stop_beacons_locked(struct ieee802154_local *local,
+                                 struct ieee802154_sub_if_data *sdata)
+{
+       struct wpan_dev *wpan_dev = &sdata->wpan_dev;
+       struct cfg802154_beacon_request *request;
+
+       ASSERT_RTNL();
+
+       if (!mac802154_is_beaconing(local))
+               return -ESRCH;
+
+       clear_bit(IEEE802154_IS_BEACONING, &local->ongoing);
+       cancel_delayed_work(&local->beacon_work);
+       request = rcu_replace_pointer(local->beacon_req, NULL, 1);
+       if (!request)
+               return 0;
+       kfree_rcu(request);
+
+       nl802154_beaconing_done(wpan_dev);
+
+       return 0;
+}
+
+int mac802154_send_beacons_locked(struct ieee802154_sub_if_data *sdata,
+                                 struct cfg802154_beacon_request *request)
+{
+       struct ieee802154_local *local = sdata->local;
+
+       ASSERT_RTNL();
+
+       if (mac802154_is_beaconing(local))
+               mac802154_stop_beacons_locked(local, sdata);
+
+       /* Store beaconing parameters */
+       rcu_assign_pointer(local->beacon_req, request);
+
+       set_bit(IEEE802154_IS_BEACONING, &local->ongoing);
+
+       memset(&local->beacon, 0, sizeof(local->beacon));
+       local->beacon.mhr.fc.type = IEEE802154_FC_TYPE_BEACON;
+       local->beacon.mhr.fc.security_enabled = 0;
+       local->beacon.mhr.fc.frame_pending = 0;
+       local->beacon.mhr.fc.ack_request = 0;
+       local->beacon.mhr.fc.intra_pan = 0;
+       local->beacon.mhr.fc.dest_addr_mode = IEEE802154_NO_ADDRESSING;
+       local->beacon.mhr.fc.version = IEEE802154_2003_STD;
+       local->beacon.mhr.fc.source_addr_mode = IEEE802154_EXTENDED_ADDRESSING;
+       atomic_set(&request->wpan_dev->bsn, -1);
+       local->beacon.mhr.source.mode = IEEE802154_ADDR_LONG;
+       local->beacon.mhr.source.pan_id = cpu_to_le16(request->wpan_dev->pan_id);
+       local->beacon.mhr.source.extended_addr = cpu_to_le64(request->wpan_dev->extended_addr);
+       local->beacon.mac_pl.beacon_order = request->interval;
+       local->beacon.mac_pl.superframe_order = request->interval;
+       local->beacon.mac_pl.final_cap_slot = 0xf;
+       local->beacon.mac_pl.battery_life_ext = 0;
+       /* TODO: Fill this field depending on the coordinator capacity */
+       local->beacon.mac_pl.pan_coordinator = 1;
+       local->beacon.mac_pl.assoc_permit = 1;
+
+       /* Start the beacon work */
+       local->beacon_interval =
+               mac802154_scan_get_channel_time(request->interval,
+                                               request->wpan_phy->symbol_duration);
+       queue_delayed_work(local->mac_wq, &local->beacon_work, 0);
+
+       return 0;
+}