From ed3557c947e1d4164d370cc2d69dd7eb92706f0a Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 3 Jan 2023 17:56:39 +0100 Subject: [PATCH 01/20] ieee802154: Add support for user scanning requests The ieee802154 layer should be able to scan a set of channels in order to look for beacons advertizing PANs. Supporting this involves adding two user commands: triggering scans and aborting scans. The user should also be notified when a new beacon is received and also upon scan termination. A scan request structure is created to list the requirements and to be accessed asynchronously when changing channels or receiving beacons. Mac layers may now implement the ->trigger_scan() and ->abort_scan() hooks. Co-developed-by: David Girault Signed-off-by: David Girault Signed-off-by: Miquel Raynal Acked-by: Alexander Aring Link: https://lore.kernel.org/r/20230103165644.432209-2-miquel.raynal@bootlin.com Signed-off-by: Stefan Schmidt --- include/linux/ieee802154.h | 3 + include/net/cfg802154.h | 25 +++++ include/net/nl802154.h | 58 ++++++++++ net/ieee802154/nl802154.c | 220 +++++++++++++++++++++++++++++++++++++ net/ieee802154/nl802154.h | 3 + net/ieee802154/rdev-ops.h | 28 +++++ net/ieee802154/trace.h | 40 +++++++ 7 files changed, 377 insertions(+) diff --git a/include/linux/ieee802154.h b/include/linux/ieee802154.h index 0303eb84d596..b22e4147d334 100644 --- a/include/linux/ieee802154.h +++ b/include/linux/ieee802154.h @@ -44,6 +44,9 @@ #define IEEE802154_SHORT_ADDR_LEN 2 #define IEEE802154_PAN_ID_LEN 2 +/* Duration in superframe order */ +#define IEEE802154_MAX_SCAN_DURATION 14 +#define IEEE802154_ACTIVE_SCAN_DURATION 15 #define IEEE802154_LIFS_PERIOD 40 #define IEEE802154_SIFS_PERIOD 12 #define IEEE802154_MAX_SIFS_FRAME_SIZE 18 diff --git a/include/net/cfg802154.h b/include/net/cfg802154.h index d09c393d229f..76d4f95e9974 100644 --- a/include/net/cfg802154.h +++ b/include/net/cfg802154.h @@ -18,6 +18,7 @@ struct wpan_phy; struct wpan_phy_cca; +struct cfg802154_scan_request; #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL struct ieee802154_llsec_device_key; @@ -67,6 +68,10 @@ struct cfg802154_ops { struct wpan_dev *wpan_dev, bool mode); int (*set_ackreq_default)(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev, bool ackreq); + int (*trigger_scan)(struct wpan_phy *wpan_phy, + struct cfg802154_scan_request *request); + int (*abort_scan)(struct wpan_phy *wpan_phy, + struct wpan_dev *wpan_dev); #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL void (*get_llsec_table)(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev, @@ -278,6 +283,26 @@ struct ieee802154_coord_desc { bool gts_permit; }; +/** + * struct cfg802154_scan_request - Scan request + * + * @type: type of scan to be performed + * @page: page on which to perform the scan + * @channels: channels in te %page to be scanned + * @duration: time spent on each channel, calculated with: + * aBaseSuperframeDuration * (2 ^ duration + 1) + * @wpan_dev: the wpan device on which to perform the scan + * @wpan_phy: the wpan phy on which to perform the scan + */ +struct cfg802154_scan_request { + enum nl802154_scan_types type; + u8 page; + u32 channels; + u8 duration; + struct wpan_dev *wpan_dev; + struct wpan_phy *wpan_phy; +}; + struct ieee802154_llsec_key_id { u8 mode; u8 id; diff --git a/include/net/nl802154.h b/include/net/nl802154.h index b79a89d5207c..c267fa1c5aac 100644 --- a/include/net/nl802154.h +++ b/include/net/nl802154.h @@ -73,6 +73,9 @@ enum nl802154_commands { NL802154_CMD_DEL_SEC_LEVEL, NL802154_CMD_SCAN_EVENT, + NL802154_CMD_TRIGGER_SCAN, + NL802154_CMD_ABORT_SCAN, + NL802154_CMD_SCAN_DONE, /* add new commands above here */ @@ -134,6 +137,13 @@ enum nl802154_attrs { NL802154_ATTR_NETNS_FD, NL802154_ATTR_COORDINATOR, + NL802154_ATTR_SCAN_TYPE, + NL802154_ATTR_SCAN_FLAGS, + NL802154_ATTR_SCAN_CHANNELS, + NL802154_ATTR_SCAN_PREAMBLE_CODES, + NL802154_ATTR_SCAN_MEAN_PRF, + NL802154_ATTR_SCAN_DURATION, + NL802154_ATTR_SCAN_DONE_REASON, /* add attributes here, update the policy in nl802154.c */ @@ -259,6 +269,54 @@ enum nl802154_coord { NL802154_COORD_MAX, }; +/** + * enum nl802154_scan_types - Scan types + * + * @__NL802154_SCAN_INVALID: scan type number 0 is reserved + * @NL802154_SCAN_ED: An ED scan allows a device to obtain a measure of the peak + * energy in each requested channel + * @NL802154_SCAN_ACTIVE: Locate any coordinator transmitting Beacon frames using + * a Beacon Request command + * @NL802154_SCAN_PASSIVE: Locate any coordinator transmitting Beacon frames + * @NL802154_SCAN_ORPHAN: Relocate coordinator following a loss of synchronisation + * @NL802154_SCAN_ENHANCED_ACTIVE: Same as Active using Enhanced Beacon Request + * command instead of Beacon Request command + * @NL802154_SCAN_RIT_PASSIVE: Passive scan for RIT Data Request command frames + * instead of Beacon frames + * @NL802154_SCAN_ATTR_MAX: Maximum SCAN attribute number + */ +enum nl802154_scan_types { + __NL802154_SCAN_INVALID, + NL802154_SCAN_ED, + NL802154_SCAN_ACTIVE, + NL802154_SCAN_PASSIVE, + NL802154_SCAN_ORPHAN, + NL802154_SCAN_ENHANCED_ACTIVE, + NL802154_SCAN_RIT_PASSIVE, + + /* keep last */ + NL802154_SCAN_ATTR_MAX, +}; + +/** + * enum nl802154_scan_done_reasons - End of scan reasons + * + * @__NL802154_SCAN_DONE_REASON_INVALID: scan done reason number 0 is reserved. + * @NL802154_SCAN_DONE_REASON_FINISHED: The scan just finished naturally after + * going through all the requested and possible (complex) channels. + * @NL802154_SCAN_DONE_REASON_ABORTED: The scan was aborted upon user request. + * a Beacon Request command + * @NL802154_SCAN_DONE_REASON_MAX: Maximum scan done reason attribute number. + */ +enum nl802154_scan_done_reasons { + __NL802154_SCAN_DONE_REASON_INVALID, + NL802154_SCAN_DONE_REASON_FINISHED, + NL802154_SCAN_DONE_REASON_ABORTED, + + /* keep last */ + NL802154_SCAN_DONE_REASON_MAX, +}; + /** * enum nl802154_cca_modes - cca modes * diff --git a/net/ieee802154/nl802154.c b/net/ieee802154/nl802154.c index 248ad5e46969..11aa693af449 100644 --- a/net/ieee802154/nl802154.c +++ b/net/ieee802154/nl802154.c @@ -221,6 +221,13 @@ static const struct nla_policy nl802154_policy[NL802154_ATTR_MAX+1] = { [NL802154_ATTR_COORDINATOR] = { .type = NLA_NESTED }, + [NL802154_ATTR_SCAN_TYPE] = { .type = NLA_U8 }, + [NL802154_ATTR_SCAN_CHANNELS] = { .type = NLA_U32 }, + [NL802154_ATTR_SCAN_PREAMBLE_CODES] = { .type = NLA_U64 }, + [NL802154_ATTR_SCAN_MEAN_PRF] = { .type = NLA_U8 }, + [NL802154_ATTR_SCAN_DURATION] = { .type = NLA_U8 }, + [NL802154_ATTR_SCAN_DONE_REASON] = { .type = NLA_U8 }, + #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL [NL802154_ATTR_SEC_ENABLED] = { .type = NLA_U8, }, [NL802154_ATTR_SEC_OUT_LEVEL] = { .type = NLA_U32, }, @@ -1384,6 +1391,203 @@ int nl802154_scan_event(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev, } EXPORT_SYMBOL_GPL(nl802154_scan_event); +static int nl802154_trigger_scan(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg802154_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct wpan_dev *wpan_dev = dev->ieee802154_ptr; + struct wpan_phy *wpan_phy = &rdev->wpan_phy; + struct cfg802154_scan_request *request; + u8 type; + int err; + + /* Monitors are not allowed to perform scans */ + if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR) + return -EPERM; + + request = kzalloc(sizeof(*request), GFP_KERNEL); + if (!request) + return -ENOMEM; + + request->wpan_dev = wpan_dev; + request->wpan_phy = wpan_phy; + + type = nla_get_u8(info->attrs[NL802154_ATTR_SCAN_TYPE]); + switch (type) { + case NL802154_SCAN_PASSIVE: + request->type = type; + break; + default: + pr_err("Unsupported scan type: %d\n", type); + err = -EINVAL; + goto free_request; + } + + if (info->attrs[NL802154_ATTR_PAGE]) { + request->page = nla_get_u8(info->attrs[NL802154_ATTR_PAGE]); + if (request->page > IEEE802154_MAX_PAGE) { + pr_err("Invalid page %d > %d\n", + request->page, IEEE802154_MAX_PAGE); + err = -EINVAL; + goto free_request; + } + } else { + /* Use current page by default */ + request->page = wpan_phy->current_page; + } + + if (info->attrs[NL802154_ATTR_SCAN_CHANNELS]) { + request->channels = nla_get_u32(info->attrs[NL802154_ATTR_SCAN_CHANNELS]); + if (request->channels >= BIT(IEEE802154_MAX_CHANNEL + 1)) { + pr_err("Invalid channels bitfield %x ≥ %lx\n", + request->channels, + BIT(IEEE802154_MAX_CHANNEL + 1)); + err = -EINVAL; + goto free_request; + } + } else { + /* Scan all supported channels by default */ + request->channels = wpan_phy->supported.channels[request->page]; + } + + if (info->attrs[NL802154_ATTR_SCAN_PREAMBLE_CODES] || + info->attrs[NL802154_ATTR_SCAN_MEAN_PRF]) { + pr_err("Preamble codes and mean PRF not supported yet\n"); + err = -EINVAL; + goto free_request; + } + + if (info->attrs[NL802154_ATTR_SCAN_DURATION]) { + request->duration = nla_get_u8(info->attrs[NL802154_ATTR_SCAN_DURATION]); + if (request->duration > IEEE802154_MAX_SCAN_DURATION) { + pr_err("Duration is out of range\n"); + err = -EINVAL; + goto free_request; + } + } else { + /* Use maximum duration order by default */ + request->duration = IEEE802154_MAX_SCAN_DURATION; + } + + if (wpan_dev->netdev) + dev_hold(wpan_dev->netdev); + + err = rdev_trigger_scan(rdev, request); + if (err) { + pr_err("Failure starting scanning (%d)\n", err); + goto free_device; + } + + return 0; + +free_device: + if (wpan_dev->netdev) + dev_put(wpan_dev->netdev); +free_request: + kfree(request); + + return err; +} + +static int nl802154_prep_scan_msg(struct sk_buff *msg, + struct cfg802154_registered_device *rdev, + struct wpan_dev *wpan_dev, u32 portid, + u32 seq, int flags, u8 cmd, u8 arg) +{ + void *hdr; + + hdr = nl802154hdr_put(msg, portid, seq, flags, cmd); + if (!hdr) + return -ENOBUFS; + + if (nla_put_u32(msg, NL802154_ATTR_WPAN_PHY, rdev->wpan_phy_idx)) + goto nla_put_failure; + + if (wpan_dev->netdev && + nla_put_u32(msg, NL802154_ATTR_IFINDEX, wpan_dev->netdev->ifindex)) + goto nla_put_failure; + + if (nla_put_u64_64bit(msg, NL802154_ATTR_WPAN_DEV, + wpan_dev_id(wpan_dev), NL802154_ATTR_PAD)) + goto nla_put_failure; + + if (cmd == NL802154_CMD_SCAN_DONE && + nla_put_u8(msg, NL802154_ATTR_SCAN_DONE_REASON, arg)) + goto nla_put_failure; + + genlmsg_end(msg, hdr); + + return 0; + +nla_put_failure: + genlmsg_cancel(msg, hdr); + + return -EMSGSIZE; +} + +static int nl802154_send_scan_msg(struct cfg802154_registered_device *rdev, + struct wpan_dev *wpan_dev, u8 cmd, u8 arg) +{ + struct sk_buff *msg; + int ret; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + ret = nl802154_prep_scan_msg(msg, rdev, wpan_dev, 0, 0, 0, cmd, arg); + if (ret < 0) { + nlmsg_free(msg); + return ret; + } + + return genlmsg_multicast_netns(&nl802154_fam, + wpan_phy_net(&rdev->wpan_phy), msg, 0, + NL802154_MCGRP_SCAN, GFP_KERNEL); +} + +int nl802154_scan_started(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev) +{ + struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(wpan_phy); + int err; + + /* Ignore errors when there are no listeners */ + err = nl802154_send_scan_msg(rdev, wpan_dev, NL802154_CMD_TRIGGER_SCAN, 0); + if (err == -ESRCH) + err = 0; + + return err; +} +EXPORT_SYMBOL_GPL(nl802154_scan_started); + +int nl802154_scan_done(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev, + enum nl802154_scan_done_reasons reason) +{ + struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(wpan_phy); + int err; + + /* Ignore errors when there are no listeners */ + err = nl802154_send_scan_msg(rdev, wpan_dev, NL802154_CMD_SCAN_DONE, reason); + if (err == -ESRCH) + err = 0; + + if (wpan_dev->netdev) + dev_put(wpan_dev->netdev); + + return err; +} +EXPORT_SYMBOL_GPL(nl802154_scan_done); + +static int nl802154_abort_scan(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg802154_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct wpan_dev *wpan_dev = dev->ieee802154_ptr; + + /* Resources are released in the notification helper above */ + return rdev_abort_scan(rdev, wpan_dev); +} + #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL static const struct nla_policy nl802154_dev_addr_policy[NL802154_DEV_ADDR_ATTR_MAX + 1] = { [NL802154_DEV_ADDR_ATTR_PAN_ID] = { .type = NLA_U16 }, @@ -2474,6 +2678,22 @@ static const struct genl_ops nl802154_ops[] = { .internal_flags = NL802154_FLAG_NEED_NETDEV | NL802154_FLAG_NEED_RTNL, }, + { + .cmd = NL802154_CMD_TRIGGER_SCAN, + .doit = nl802154_trigger_scan, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL802154_FLAG_NEED_NETDEV | + NL802154_FLAG_CHECK_NETDEV_UP | + NL802154_FLAG_NEED_RTNL, + }, + { + .cmd = NL802154_CMD_ABORT_SCAN, + .doit = nl802154_abort_scan, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL802154_FLAG_NEED_NETDEV | + NL802154_FLAG_CHECK_NETDEV_UP | + NL802154_FLAG_NEED_RTNL, + }, #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL { .cmd = NL802154_CMD_SET_SEC_PARAMS, diff --git a/net/ieee802154/nl802154.h b/net/ieee802154/nl802154.h index 89b805500032..cfa7134be747 100644 --- a/net/ieee802154/nl802154.h +++ b/net/ieee802154/nl802154.h @@ -6,5 +6,8 @@ int nl802154_init(void); void nl802154_exit(void); int nl802154_scan_event(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev, struct ieee802154_coord_desc *desc); +int nl802154_scan_started(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev); +int nl802154_scan_done(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev, + enum nl802154_scan_done_reasons reason); #endif /* __IEEE802154_NL802154_H */ diff --git a/net/ieee802154/rdev-ops.h b/net/ieee802154/rdev-ops.h index 598f5af49775..e171d74c3251 100644 --- a/net/ieee802154/rdev-ops.h +++ b/net/ieee802154/rdev-ops.h @@ -209,6 +209,34 @@ rdev_set_ackreq_default(struct cfg802154_registered_device *rdev, return ret; } +static inline int rdev_trigger_scan(struct cfg802154_registered_device *rdev, + struct cfg802154_scan_request *request) +{ + int ret; + + if (!rdev->ops->trigger_scan) + return -EOPNOTSUPP; + + trace_802154_rdev_trigger_scan(&rdev->wpan_phy, request); + ret = rdev->ops->trigger_scan(&rdev->wpan_phy, request); + trace_802154_rdev_return_int(&rdev->wpan_phy, ret); + return ret; +} + +static inline int rdev_abort_scan(struct cfg802154_registered_device *rdev, + struct wpan_dev *wpan_dev) +{ + int ret; + + if (!rdev->ops->abort_scan) + return -EOPNOTSUPP; + + trace_802154_rdev_abort_scan(&rdev->wpan_phy, wpan_dev); + ret = rdev->ops->abort_scan(&rdev->wpan_phy, wpan_dev); + trace_802154_rdev_return_int(&rdev->wpan_phy, ret); + return ret; +} + #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL /* TODO this is already a nl802154, so move into ieee802154 */ static inline void diff --git a/net/ieee802154/trace.h b/net/ieee802154/trace.h index 19c2e5d60e76..e5405f737ded 100644 --- a/net/ieee802154/trace.h +++ b/net/ieee802154/trace.h @@ -295,6 +295,46 @@ TRACE_EVENT(802154_rdev_set_ackreq_default, WPAN_DEV_PR_ARG, BOOL_TO_STR(__entry->ackreq)) ); +TRACE_EVENT(802154_rdev_trigger_scan, + TP_PROTO(struct wpan_phy *wpan_phy, + struct cfg802154_scan_request *request), + TP_ARGS(wpan_phy, request), + TP_STRUCT__entry( + WPAN_PHY_ENTRY + __field(u8, page) + __field(u32, channels) + __field(u8, duration) + ), + TP_fast_assign( + WPAN_PHY_ASSIGN; + __entry->page = request->page; + __entry->channels = request->channels; + __entry->duration = request->duration; + ), + TP_printk(WPAN_PHY_PR_FMT ", scan, page: %d, channels: %x, duration %d", + WPAN_PHY_PR_ARG, __entry->page, __entry->channels, __entry->duration) +); + +DECLARE_EVENT_CLASS(802154_wdev_template, + TP_PROTO(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev), + TP_ARGS(wpan_phy, wpan_dev), + TP_STRUCT__entry( + WPAN_PHY_ENTRY + WPAN_DEV_ENTRY + ), + TP_fast_assign( + WPAN_PHY_ASSIGN; + WPAN_DEV_ASSIGN; + ), + TP_printk(WPAN_PHY_PR_FMT ", " WPAN_DEV_PR_FMT, + WPAN_PHY_PR_ARG, WPAN_DEV_PR_ARG) +); + +DEFINE_EVENT(802154_wdev_template, 802154_rdev_abort_scan, + TP_PROTO(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev), + TP_ARGS(wpan_phy, wpan_dev) +); + TRACE_EVENT(802154_rdev_return_int, TP_PROTO(struct wpan_phy *wpan_phy, int ret), TP_ARGS(wpan_phy, ret), From 44def58f5835bbfaf81902c88460fd86a551f4b7 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 3 Jan 2023 17:56:40 +0100 Subject: [PATCH 02/20] ieee802154: Define a beacon frame header This definition will be used when adding support for scanning and defines the content of a beacon frame header as in the 802.15.4 specification. Signed-off-by: Miquel Raynal Acked-by: Alexander Aring Link: https://lore.kernel.org/r/20230103165644.432209-3-miquel.raynal@bootlin.com Signed-off-by: Stefan Schmidt --- include/net/ieee802154_netdev.h | 36 +++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/include/net/ieee802154_netdev.h b/include/net/ieee802154_netdev.h index 4c33a20ea57f..2f2196049a86 100644 --- a/include/net/ieee802154_netdev.h +++ b/include/net/ieee802154_netdev.h @@ -38,6 +38,42 @@ #include +struct ieee802154_beacon_hdr { +#if defined(__LITTLE_ENDIAN_BITFIELD) + u16 beacon_order:4, + superframe_order:4, + final_cap_slot:4, + battery_life_ext:1, + reserved0:1, + pan_coordinator:1, + assoc_permit:1; + u8 gts_count:3, + gts_reserved:4, + gts_permit:1; + u8 pend_short_addr_count:3, + reserved1:1, + pend_ext_addr_count:3, + reserved2:1; +#elif defined(__BIG_ENDIAN_BITFIELD) + u16 assoc_permit:1, + pan_coordinator:1, + reserved0:1, + battery_life_ext:1, + final_cap_slot:4, + superframe_order:4, + beacon_order:4; + u8 gts_permit:1, + gts_reserved:4, + gts_count:3; + u8 reserved2:1, + pend_ext_addr_count:3, + reserved1:1, + pend_short_addr_count:3; +#else +#error "Please fix " +#endif +} __packed; + struct ieee802154_sechdr { #if defined(__LITTLE_ENDIAN_BITFIELD) u8 level:3, From d2aaf2a01792ccf214f933d0b1ca2d41788c7b16 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 3 Jan 2023 17:56:41 +0100 Subject: [PATCH 03/20] ieee802154: Introduce a helper to validate a channel This helper for now only checks if the page member and channel member are valid (in the specification range) and supported (by checking the device capabilities). Soon two new parameters will be introduced and having this helper will let us only modify its content rather than modifying the logic everywhere else in the subsystem. There is not functional change. Signed-off-by: Miquel Raynal Acked-by: Alexander Aring Link: https://lore.kernel.org/r/20230103165644.432209-4-miquel.raynal@bootlin.com Signed-off-by: Stefan Schmidt --- include/net/cfg802154.h | 11 +++++++++++ net/ieee802154/nl802154.c | 3 +-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/include/net/cfg802154.h b/include/net/cfg802154.h index 76d4f95e9974..1184b543fba7 100644 --- a/include/net/cfg802154.h +++ b/include/net/cfg802154.h @@ -246,6 +246,17 @@ static inline void wpan_phy_net_set(struct wpan_phy *wpan_phy, struct net *net) write_pnet(&wpan_phy->_net, net); } +static inline bool ieee802154_chan_is_valid(struct wpan_phy *phy, + u8 page, u8 channel) +{ + if (page > IEEE802154_MAX_PAGE || + channel > IEEE802154_MAX_CHANNEL || + !(phy->supported.channels[page] & BIT(channel))) + return false; + + return true; +} + /** * struct ieee802154_addr - IEEE802.15.4 device address * @mode: Address mode from frame header. Can be one of: diff --git a/net/ieee802154/nl802154.c b/net/ieee802154/nl802154.c index 11aa693af449..0b7a9f16b3b6 100644 --- a/net/ieee802154/nl802154.c +++ b/net/ieee802154/nl802154.c @@ -976,8 +976,7 @@ static int nl802154_set_channel(struct sk_buff *skb, struct genl_info *info) channel = nla_get_u8(info->attrs[NL802154_ATTR_CHANNEL]); /* check 802.15.4 constraints */ - if (page > IEEE802154_MAX_PAGE || channel > IEEE802154_MAX_CHANNEL || - !(rdev->wpan_phy.supported.channels[page] & BIT(channel))) + if (!ieee802154_chan_is_valid(&rdev->wpan_phy, page, channel)) return -EINVAL; return rdev_set_channel(rdev, page, channel); From 5755cd4d9432779027771e43e51d81a2994ed795 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 3 Jan 2023 17:56:42 +0100 Subject: [PATCH 04/20] mac802154: Prepare forcing specific symbol duration The scan logic will bypass the whole ->set_channel() logic from the top by calling the driver hook to just switch between channels when required. We can no longer rely on the "current" page/channel settings to set the right symbol duration. Let's add these as new parameters to allow providing the page/channel couple that we want. There is no functional change. Signed-off-by: Miquel Raynal Acked-by: Alexander Aring Link: https://lore.kernel.org/r/20230103165644.432209-5-miquel.raynal@bootlin.com Signed-off-by: Stefan Schmidt --- include/net/cfg802154.h | 3 ++- net/mac802154/cfg.c | 2 +- net/mac802154/main.c | 20 +++++++++++--------- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/include/net/cfg802154.h b/include/net/cfg802154.h index 1184b543fba7..c16ae5d2dc86 100644 --- a/include/net/cfg802154.h +++ b/include/net/cfg802154.h @@ -483,6 +483,7 @@ static inline const char *wpan_phy_name(struct wpan_phy *phy) return dev_name(&phy->dev); } -void ieee802154_configure_durations(struct wpan_phy *phy); +void ieee802154_configure_durations(struct wpan_phy *phy, + unsigned int page, unsigned int channel); #endif /* __NET_CFG802154_H */ diff --git a/net/mac802154/cfg.c b/net/mac802154/cfg.c index dc2d918fac68..469d6e8dd2dd 100644 --- a/net/mac802154/cfg.c +++ b/net/mac802154/cfg.c @@ -118,7 +118,7 @@ ieee802154_set_channel(struct wpan_phy *wpan_phy, u8 page, u8 channel) if (!ret) { wpan_phy->current_page = page; wpan_phy->current_channel = channel; - ieee802154_configure_durations(wpan_phy); + ieee802154_configure_durations(wpan_phy, page, channel); } return ret; diff --git a/net/mac802154/main.c b/net/mac802154/main.c index 3ed31daf7b9c..12a13a850fdf 100644 --- a/net/mac802154/main.c +++ b/net/mac802154/main.c @@ -113,32 +113,33 @@ ieee802154_alloc_hw(size_t priv_data_len, const struct ieee802154_ops *ops) } EXPORT_SYMBOL(ieee802154_alloc_hw); -void ieee802154_configure_durations(struct wpan_phy *phy) +void ieee802154_configure_durations(struct wpan_phy *phy, + unsigned int page, unsigned int channel) { u32 duration = 0; - switch (phy->current_page) { + switch (page) { case 0: - if (BIT(phy->current_channel) & 0x1) + if (BIT(channel) & 0x1) /* 868 MHz BPSK 802.15.4-2003: 20 ksym/s */ duration = 50 * NSEC_PER_USEC; - else if (BIT(phy->current_channel) & 0x7FE) + else if (BIT(channel) & 0x7FE) /* 915 MHz BPSK 802.15.4-2003: 40 ksym/s */ duration = 25 * NSEC_PER_USEC; - else if (BIT(phy->current_channel) & 0x7FFF800) + else if (BIT(channel) & 0x7FFF800) /* 2400 MHz O-QPSK 802.15.4-2006: 62.5 ksym/s */ duration = 16 * NSEC_PER_USEC; break; case 2: - if (BIT(phy->current_channel) & 0x1) + if (BIT(channel) & 0x1) /* 868 MHz O-QPSK 802.15.4-2006: 25 ksym/s */ duration = 40 * NSEC_PER_USEC; - else if (BIT(phy->current_channel) & 0x7FE) + else if (BIT(channel) & 0x7FE) /* 915 MHz O-QPSK 802.15.4-2006: 62.5 ksym/s */ duration = 16 * NSEC_PER_USEC; break; case 3: - if (BIT(phy->current_channel) & 0x3FFF) + if (BIT(channel) & 0x3FFF) /* 2.4 GHz CSS 802.15.4a-2007: 1/6 Msym/s */ duration = 6 * NSEC_PER_USEC; break; @@ -201,7 +202,8 @@ int ieee802154_register_hw(struct ieee802154_hw *hw) ieee802154_setup_wpan_phy_pib(local->phy); - ieee802154_configure_durations(local->phy); + ieee802154_configure_durations(local->phy, local->phy->current_page, + local->phy->current_channel); if (!(hw->flags & IEEE802154_HW_CSMA_PARAMS)) { local->phy->supported.min_csma_backoffs = 4; From dd18096256c89612e3eb858de748c29d10e8f3e7 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 3 Jan 2023 17:56:43 +0100 Subject: [PATCH 05/20] mac802154: Add MLME Tx locked helpers These have the exact same behavior as before, except they expect the rtnl to be already taken (and will complain otherwise). This allows performing MLME transmissions from different contexts. Signed-off-by: Miquel Raynal Acked-by: Alexander Aring Link: https://lore.kernel.org/r/20230103165644.432209-6-miquel.raynal@bootlin.com Signed-off-by: Stefan Schmidt --- net/mac802154/ieee802154_i.h | 6 ++++ net/mac802154/tx.c | 58 +++++++++++++++++++++++------------- 2 files changed, 43 insertions(+), 21 deletions(-) diff --git a/net/mac802154/ieee802154_i.h b/net/mac802154/ieee802154_i.h index 509e0172fe82..aeadee543a9c 100644 --- a/net/mac802154/ieee802154_i.h +++ b/net/mac802154/ieee802154_i.h @@ -141,10 +141,16 @@ int ieee802154_mlme_op_pre(struct ieee802154_local *local); int ieee802154_mlme_tx(struct ieee802154_local *local, struct ieee802154_sub_if_data *sdata, struct sk_buff *skb); +int ieee802154_mlme_tx_locked(struct ieee802154_local *local, + struct ieee802154_sub_if_data *sdata, + struct sk_buff *skb); void ieee802154_mlme_op_post(struct ieee802154_local *local); int ieee802154_mlme_tx_one(struct ieee802154_local *local, struct ieee802154_sub_if_data *sdata, struct sk_buff *skb); +int ieee802154_mlme_tx_one_locked(struct ieee802154_local *local, + struct ieee802154_sub_if_data *sdata, + struct sk_buff *skb); netdev_tx_t ieee802154_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev); netdev_tx_t diff --git a/net/mac802154/tx.c b/net/mac802154/tx.c index 9d8d43cf1e64..2a6f1ed763c9 100644 --- a/net/mac802154/tx.c +++ b/net/mac802154/tx.c @@ -137,34 +137,37 @@ int ieee802154_mlme_op_pre(struct ieee802154_local *local) return ieee802154_sync_and_hold_queue(local); } +int ieee802154_mlme_tx_locked(struct ieee802154_local *local, + struct ieee802154_sub_if_data *sdata, + struct sk_buff *skb) +{ + /* Avoid possible calls to ->ndo_stop() when we asynchronously perform + * MLME transmissions. + */ + ASSERT_RTNL(); + + /* Ensure the device was not stopped, otherwise error out */ + if (!local->open_count) + return -ENETDOWN; + + /* Warn if the ieee802154 core thinks MLME frames can be sent while the + * net interface expects this cannot happen. + */ + if (WARN_ON_ONCE(!netif_running(sdata->dev))) + return -ENETDOWN; + + ieee802154_tx(local, skb); + return ieee802154_sync_queue(local); +} + int ieee802154_mlme_tx(struct ieee802154_local *local, struct ieee802154_sub_if_data *sdata, struct sk_buff *skb) { int ret; - /* Avoid possible calls to ->ndo_stop() when we asynchronously perform - * MLME transmissions. - */ rtnl_lock(); - - /* Ensure the device was not stopped, otherwise error out */ - if (!local->open_count) { - rtnl_unlock(); - return -ENETDOWN; - } - - /* Warn if the ieee802154 core thinks MLME frames can be sent while the - * net interface expects this cannot happen. - */ - if (WARN_ON_ONCE(!netif_running(sdata->dev))) { - rtnl_unlock(); - return -ENETDOWN; - } - - ieee802154_tx(local, skb); - ret = ieee802154_sync_queue(local); - + ret = ieee802154_mlme_tx_locked(local, sdata, skb); rtnl_unlock(); return ret; @@ -188,6 +191,19 @@ int ieee802154_mlme_tx_one(struct ieee802154_local *local, return ret; } +int ieee802154_mlme_tx_one_locked(struct ieee802154_local *local, + struct ieee802154_sub_if_data *sdata, + struct sk_buff *skb) +{ + int ret; + + ieee802154_mlme_op_pre(local); + ret = ieee802154_mlme_tx_locked(local, sdata, skb); + ieee802154_mlme_op_post(local); + + return ret; +} + static bool ieee802154_queue_is_stopped(struct ieee802154_local *local) { return test_bit(WPAN_PHY_FLAG_STATE_QUEUE_STOPPED, &local->phy->flags); From 57588c71177f0bfc08509c2c3a9bfe32850c0786 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 3 Jan 2023 17:56:44 +0100 Subject: [PATCH 06/20] mac802154: Handle passive scanning Implement the core hooks in order to provide the softMAC layer support for passive scans. Scans are requested by the user and can be aborted. Changing channels manually is prohibited during scans. The implementation uses a workqueue triggered at a certain interval depending on the symbol duration for the current channel and the duration order provided. More advanced drivers with internal scheduling capabilities might require additional care but there is none mainline yet. Received beacons during a passive scan are processed in a work queue and their result forwarded to the upper layer. Active scanning is not supported yet. Co-developed-by: David Girault Signed-off-by: David Girault Signed-off-by: Miquel Raynal Acked-by: Alexander Aring Link: https://lore.kernel.org/r/20230103165644.432209-7-miquel.raynal@bootlin.com Signed-off-by: Stefan Schmidt --- include/linux/ieee802154.h | 4 + include/net/cfg802154.h | 16 ++ net/mac802154/Makefile | 2 +- net/mac802154/cfg.c | 31 ++++ net/mac802154/ieee802154_i.h | 37 ++++- net/mac802154/iface.c | 3 + net/mac802154/main.c | 16 +- net/mac802154/rx.c | 36 ++++- net/mac802154/scan.c | 288 +++++++++++++++++++++++++++++++++++ 9 files changed, 427 insertions(+), 6 deletions(-) create mode 100644 net/mac802154/scan.c diff --git a/include/linux/ieee802154.h b/include/linux/ieee802154.h index b22e4147d334..140f61ec0f5f 100644 --- a/include/linux/ieee802154.h +++ b/include/linux/ieee802154.h @@ -47,6 +47,10 @@ /* Duration in superframe order */ #define IEEE802154_MAX_SCAN_DURATION 14 #define IEEE802154_ACTIVE_SCAN_DURATION 15 +/* Superframe duration in slots */ +#define IEEE802154_SUPERFRAME_PERIOD 16 +/* Various periods expressed in symbols */ +#define IEEE802154_SLOT_PERIOD 60 #define IEEE802154_LIFS_PERIOD 40 #define IEEE802154_SIFS_PERIOD 12 #define IEEE802154_MAX_SIFS_FRAME_SIZE 18 diff --git a/include/net/cfg802154.h b/include/net/cfg802154.h index c16ae5d2dc86..0b0f81a945b6 100644 --- a/include/net/cfg802154.h +++ b/include/net/cfg802154.h @@ -314,6 +314,22 @@ struct cfg802154_scan_request { struct wpan_phy *wpan_phy; }; +/** + * struct cfg802154_mac_pkt - MAC packet descriptor (beacon/command) + * @node: MAC packets to process list member + * @skb: the received sk_buff + * @sdata: the interface on which @skb was received + * @page: page configuration when @skb was received + * @channel: channel configuration when @skb was received + */ +struct cfg802154_mac_pkt { + struct list_head node; + struct sk_buff *skb; + struct ieee802154_sub_if_data *sdata; + u8 page; + u8 channel; +}; + struct ieee802154_llsec_key_id { u8 mode; u8 id; diff --git a/net/mac802154/Makefile b/net/mac802154/Makefile index 4059295fdbf8..43d1347b37ee 100644 --- a/net/mac802154/Makefile +++ b/net/mac802154/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_MAC802154) += mac802154.o mac802154-objs := main.o rx.o tx.o mac_cmd.o mib.o \ - iface.o llsec.o util.o cfg.o trace.o + iface.o llsec.o util.o cfg.o scan.o trace.o CFLAGS_trace.o := -I$(src) diff --git a/net/mac802154/cfg.c b/net/mac802154/cfg.c index 469d6e8dd2dd..187cebcaf233 100644 --- a/net/mac802154/cfg.c +++ b/net/mac802154/cfg.c @@ -114,6 +114,10 @@ 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)) + return -EBUSY; + ret = drv_set_channel(local, page, channel); if (!ret) { wpan_phy->current_page = page; @@ -261,6 +265,31 @@ ieee802154_set_ackreq_default(struct wpan_phy *wpan_phy, return 0; } +static int mac802154_trigger_scan(struct wpan_phy *wpan_phy, + struct cfg802154_scan_request *request) +{ + struct ieee802154_sub_if_data *sdata; + + sdata = IEEE802154_WPAN_DEV_TO_SUB_IF(request->wpan_dev); + + ASSERT_RTNL(); + + return mac802154_trigger_scan_locked(sdata, request); +} + +static int mac802154_abort_scan(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_abort_scan_locked(local, sdata); +} + #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL static void ieee802154_get_llsec_table(struct wpan_phy *wpan_phy, @@ -468,6 +497,8 @@ const struct cfg802154_ops mac802154_config_ops = { .set_max_frame_retries = ieee802154_set_max_frame_retries, .set_lbt_mode = ieee802154_set_lbt_mode, .set_ackreq_default = ieee802154_set_ackreq_default, + .trigger_scan = mac802154_trigger_scan, + .abort_scan = mac802154_abort_scan, #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL .get_llsec_table = ieee802154_get_llsec_table, .lock_llsec_table = ieee802154_lock_llsec_table, diff --git a/net/mac802154/ieee802154_i.h b/net/mac802154/ieee802154_i.h index aeadee543a9c..0e4db967bd1d 100644 --- a/net/mac802154/ieee802154_i.h +++ b/net/mac802154/ieee802154_i.h @@ -21,6 +21,10 @@ #include "llsec.h" +enum ieee802154_ongoing { + IEEE802154_IS_SCANNING = BIT(0), +}; + /* mac802154 device private data */ struct ieee802154_local { struct ieee802154_hw hw; @@ -43,15 +47,26 @@ struct ieee802154_local { struct list_head interfaces; struct mutex iflist_mtx; - /* This one is used for scanning and other jobs not to be interfered - * with serial driver. - */ + /* Data related workqueue */ struct workqueue_struct *workqueue; + /* MAC commands related workqueue */ + struct workqueue_struct *mac_wq; struct hrtimer ifs_timer; + /* Scanning */ + u8 scan_page; + u8 scan_channel; + struct cfg802154_scan_request __rcu *scan_req; + struct delayed_work scan_work; + + /* Asynchronous tasks */ + struct list_head rx_beacon_list; + struct work_struct rx_beacon_work; + bool started; bool suspended; + unsigned long ongoing; struct tasklet_struct tasklet; struct sk_buff_head skb_queue; @@ -226,6 +241,22 @@ void mac802154_unlock_table(struct net_device *dev); int mac802154_wpan_update_llsec(struct net_device *dev); +/* PAN management handling */ +void mac802154_scan_worker(struct work_struct *work); +int mac802154_trigger_scan_locked(struct ieee802154_sub_if_data *sdata, + struct cfg802154_scan_request *request); +int mac802154_abort_scan_locked(struct ieee802154_local *local, + struct ieee802154_sub_if_data *sdata); +int mac802154_process_beacon(struct ieee802154_local *local, + struct sk_buff *skb, + u8 page, u8 channel); +void mac802154_rx_beacon_worker(struct work_struct *work); + +static inline bool mac802154_is_scanning(struct ieee802154_local *local) +{ + return test_bit(IEEE802154_IS_SCANNING, &local->ongoing); +} + /* interface handling */ int ieee802154_iface_init(void); void ieee802154_iface_exit(void); diff --git a/net/mac802154/iface.c b/net/mac802154/iface.c index 7de2f843379c..a5958d323ea3 100644 --- a/net/mac802154/iface.c +++ b/net/mac802154/iface.c @@ -302,6 +302,9 @@ static int mac802154_slave_close(struct net_device *dev) ASSERT_RTNL(); + if (mac802154_is_scanning(local)) + mac802154_abort_scan_locked(local, sdata); + netif_stop_queue(dev); local->open_count--; diff --git a/net/mac802154/main.c b/net/mac802154/main.c index 12a13a850fdf..b1111279e06d 100644 --- a/net/mac802154/main.c +++ b/net/mac802154/main.c @@ -89,6 +89,7 @@ ieee802154_alloc_hw(size_t priv_data_len, const struct ieee802154_ops *ops) local->ops = ops; INIT_LIST_HEAD(&local->interfaces); + INIT_LIST_HEAD(&local->rx_beacon_list); mutex_init(&local->iflist_mtx); tasklet_setup(&local->tasklet, ieee802154_tasklet_handler); @@ -96,6 +97,8 @@ ieee802154_alloc_hw(size_t priv_data_len, const struct ieee802154_ops *ops) skb_queue_head_init(&local->skb_queue); 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 supported flags with 802.15.4 default ranges */ phy->supported.max_minbe = 8; @@ -185,6 +188,7 @@ static void ieee802154_setup_wpan_phy_pib(struct wpan_phy *wpan_phy) int ieee802154_register_hw(struct ieee802154_hw *hw) { struct ieee802154_local *local = hw_to_local(hw); + char mac_wq_name[IFNAMSIZ + 10] = {}; struct net_device *dev; int rc = -ENOSYS; @@ -195,6 +199,13 @@ int ieee802154_register_hw(struct ieee802154_hw *hw) goto out; } + snprintf(mac_wq_name, IFNAMSIZ + 10, "%s-mac-cmds", wpan_phy_name(local->phy)); + local->mac_wq = create_singlethread_workqueue(mac_wq_name); + if (!local->mac_wq) { + rc = -ENOMEM; + goto out_wq; + } + hrtimer_init(&local->ifs_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); local->ifs_timer.function = ieee802154_xmit_ifs_timer; @@ -224,7 +235,7 @@ int ieee802154_register_hw(struct ieee802154_hw *hw) rc = wpan_phy_register(local->phy); if (rc < 0) - goto out_wq; + goto out_mac_wq; rtnl_lock(); @@ -243,6 +254,8 @@ int ieee802154_register_hw(struct ieee802154_hw *hw) out_phy: wpan_phy_unregister(local->phy); +out_mac_wq: + destroy_workqueue(local->mac_wq); out_wq: destroy_workqueue(local->workqueue); out: @@ -263,6 +276,7 @@ void ieee802154_unregister_hw(struct ieee802154_hw *hw) rtnl_unlock(); + destroy_workqueue(local->mac_wq); destroy_workqueue(local->workqueue); wpan_phy_unregister(local->phy); } diff --git a/net/mac802154/rx.c b/net/mac802154/rx.c index c2aae2a6d6a6..2b0a80571097 100644 --- a/net/mac802154/rx.c +++ b/net/mac802154/rx.c @@ -29,12 +29,31 @@ static int ieee802154_deliver_skb(struct sk_buff *skb) return netif_receive_skb(skb); } +void mac802154_rx_beacon_worker(struct work_struct *work) +{ + struct ieee802154_local *local = + container_of(work, struct ieee802154_local, rx_beacon_work); + struct cfg802154_mac_pkt *mac_pkt; + + mac_pkt = list_first_entry_or_null(&local->rx_beacon_list, + struct cfg802154_mac_pkt, node); + if (!mac_pkt) + return; + + mac802154_process_beacon(local, mac_pkt->skb, mac_pkt->page, mac_pkt->channel); + + list_del(&mac_pkt->node); + kfree_skb(mac_pkt->skb); + kfree(mac_pkt); +} + static int ieee802154_subif_frame(struct ieee802154_sub_if_data *sdata, struct sk_buff *skb, const struct ieee802154_hdr *hdr) { - struct wpan_dev *wpan_dev = &sdata->wpan_dev; struct wpan_phy *wpan_phy = sdata->local->hw.phy; + struct wpan_dev *wpan_dev = &sdata->wpan_dev; + struct cfg802154_mac_pkt *mac_pkt; __le16 span, sshort; int rc; @@ -106,6 +125,21 @@ ieee802154_subif_frame(struct ieee802154_sub_if_data *sdata, switch (mac_cb(skb)->type) { case IEEE802154_FC_TYPE_BEACON: + dev_dbg(&sdata->dev->dev, "BEACON received\n"); + if (!mac802154_is_scanning(sdata->local)) + goto fail; + + mac_pkt = kzalloc(sizeof(*mac_pkt), GFP_ATOMIC); + if (!mac_pkt) + goto fail; + + mac_pkt->skb = skb_get(skb); + mac_pkt->sdata = sdata; + mac_pkt->page = sdata->local->scan_page; + mac_pkt->channel = sdata->local->scan_channel; + list_add_tail(&mac_pkt->node, &sdata->local->rx_beacon_list); + queue_work(sdata->local->mac_wq, &sdata->local->rx_beacon_work); + return NET_RX_SUCCESS; case IEEE802154_FC_TYPE_ACK: case IEEE802154_FC_TYPE_MAC_CMD: goto fail; diff --git a/net/mac802154/scan.c b/net/mac802154/scan.c new file mode 100644 index 000000000000..56056b9c93c1 --- /dev/null +++ b/net/mac802154/scan.c @@ -0,0 +1,288 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * IEEE 802.15.4 scanning management + * + * Copyright (C) 2021 Qorvo US, Inc + * Authors: + * - David Girault + * - Miquel Raynal + */ + +#include +#include +#include + +#include "ieee802154_i.h" +#include "driver-ops.h" +#include "../ieee802154/nl802154.h" + +/* 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. + */ +static int 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; + kfree_rcu(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); + + /* Cleanup software stack */ + ieee802154_mlme_op_post(local); + + /* 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(); + + if (!mac802154_is_scanning(local)) + return -ESRCH; + + return mac802154_scan_cleanup_locked(local, sdata, true); +} + +static unsigned int mac802154_scan_get_channel_time(u8 duration_order, + u8 symbol_duration) +{ + u64 base_super_frame_duration = (u64)symbol_duration * + IEEE802154_SUPERFRAME_PERIOD * IEEE802154_SLOT_PERIOD; + + return usecs_to_jiffies(base_super_frame_duration * + (BIT(duration_order) + 1)); +} + +static void mac802154_flush_queued_beacons(struct ieee802154_local *local) +{ + struct cfg802154_mac_pkt *mac_pkt, *tmp; + + list_for_each_entry_safe(mac_pkt, tmp, &local->rx_beacon_list, node) { + list_del(&mac_pkt->node); + kfree_skb(mac_pkt->skb); + kfree(mac_pkt); + } +} + +static void +mac802154_scan_get_next_channel(struct ieee802154_local *local, + struct cfg802154_scan_request *scan_req, + u8 *channel) +{ + (*channel)++; + *channel = find_next_bit((const unsigned long *)&scan_req->channels, + IEEE802154_MAX_CHANNEL + 1, + *channel); +} + +static int mac802154_scan_find_next_chan(struct ieee802154_local *local, + struct cfg802154_scan_request *scan_req, + u8 page, u8 *channel) +{ + mac802154_scan_get_next_channel(local, scan_req, channel); + if (*channel > IEEE802154_MAX_CHANNEL) + return -EINVAL; + + return 0; +} + +void mac802154_scan_worker(struct work_struct *work) +{ + struct ieee802154_local *local = + container_of(work, struct ieee802154_local, scan_work.work); + struct cfg802154_scan_request *scan_req; + struct ieee802154_sub_if_data *sdata; + unsigned int scan_duration = 0; + struct wpan_phy *wpan_phy; + u8 scan_req_duration; + u8 page, channel; + int ret; + + /* 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; + } + + sdata = IEEE802154_WPAN_DEV_TO_SUB_IF(scan_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->scan_work, + msecs_to_jiffies(1000)); + return; + } + + wpan_phy = scan_req->wpan_phy; + scan_req_duration = scan_req->duration; + + /* 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; + } + + local->scan_page = page; + local->scan_channel = channel; + + 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; + } + + ieee802154_configure_durations(wpan_phy, page, channel); + scan_duration = mac802154_scan_get_channel_time(scan_req_duration, + wpan_phy->symbol_duration); + dev_dbg(&sdata->dev->dev, + "Scan page %u channel %u for %ums\n", + page, channel, jiffies_to_msecs(scan_duration)); + queue_delayed_work(local->mac_wq, &local->scan_work, scan_duration); + return; + +end_scan: + rtnl_lock(); + mac802154_scan_cleanup_locked(local, sdata, false); + rtnl_unlock(); +} + +int mac802154_trigger_scan_locked(struct ieee802154_sub_if_data *sdata, + struct cfg802154_scan_request *request) +{ + struct ieee802154_local *local = sdata->local; + + ASSERT_RTNL(); + + if (mac802154_is_scanning(local)) + return -EBUSY; + + /* TODO: support other scanning type */ + if (request->type != NL802154_SCAN_PASSIVE) + 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); + + sdata->required_filtering = IEEE802154_FILTERING_3_SCAN; + local->scan_page = request->page; + local->scan_channel = -1; + set_bit(IEEE802154_IS_SCANNING, &local->ongoing); + + nl802154_scan_started(request->wpan_phy, request->wpan_dev); + + queue_delayed_work(local->mac_wq, &local->scan_work, 0); + + return 0; +} + +int mac802154_process_beacon(struct ieee802154_local *local, + struct sk_buff *skb, + u8 page, u8 channel) +{ + struct ieee802154_beacon_hdr *bh = (void *)skb->data; + struct ieee802154_addr *src = &mac_cb(skb)->source; + struct cfg802154_scan_request *scan_req; + struct ieee802154_coord_desc desc; + + if (skb->len != sizeof(*bh)) + return -EINVAL; + + if (unlikely(src->mode == IEEE802154_ADDR_NONE)) + return -EINVAL; + + dev_dbg(&skb->dev->dev, + "BEACON received on page %u channel %u\n", + page, channel); + + memcpy(&desc.addr, src, sizeof(desc.addr)); + desc.page = page; + desc.channel = channel; + desc.link_quality = mac_cb(skb)->lqi; + desc.superframe_spec = get_unaligned_le16(skb->data); + desc.gts_permit = bh->gts_permit; + + trace_802154_scan_event(&desc); + + rcu_read_lock(); + scan_req = rcu_dereference(local->scan_req); + if (likely(scan_req)) + nl802154_scan_event(scan_req->wpan_phy, scan_req->wpan_dev, &desc); + rcu_read_unlock(); + + return 0; +} From 9bc114504b07207d671593f6f6d787d55dcf91bd Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 25 Jan 2023 11:29:22 +0100 Subject: [PATCH 07/20] ieee802154: Add support for user beaconing requests Parse user requests for sending beacons, start sending beacons at a regular pace. If needed, the pace can be updated with a new request. The process can also be interrupted at any moment. The page and channel must be changed beforehands if needed. Interval orders above 14 are reserved to tell a device it must answer BEACON_REQ coming from another device as part of an active scan procedure and this is not yet supported. A netlink "beacon request" structure is created to list the requirements. Mac layers may now implement the ->send_beacons() and ->stop_beacons() hooks. Co-developed-by: David Girault Signed-off-by: David Girault Signed-off-by: Miquel Raynal Acked-by: Alexander Aring Link: https://lore.kernel.org/r/20230125102923.135465-2-miquel.raynal@bootlin.com Signed-off-by: Stefan Schmidt --- include/net/cfg802154.h | 23 ++++++++++ include/net/nl802154.h | 3 ++ net/ieee802154/nl802154.c | 93 +++++++++++++++++++++++++++++++++++++++ net/ieee802154/nl802154.h | 1 + net/ieee802154/rdev-ops.h | 28 ++++++++++++ net/ieee802154/trace.h | 21 +++++++++ 6 files changed, 169 insertions(+) diff --git a/include/net/cfg802154.h b/include/net/cfg802154.h index 0b0f81a945b6..0c2778a836db 100644 --- a/include/net/cfg802154.h +++ b/include/net/cfg802154.h @@ -19,6 +19,7 @@ struct wpan_phy; struct wpan_phy_cca; struct cfg802154_scan_request; +struct cfg802154_beacon_request; #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL struct ieee802154_llsec_device_key; @@ -72,6 +73,10 @@ struct cfg802154_ops { struct cfg802154_scan_request *request); int (*abort_scan)(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev); + int (*send_beacons)(struct wpan_phy *wpan_phy, + struct cfg802154_beacon_request *request); + int (*stop_beacons)(struct wpan_phy *wpan_phy, + struct wpan_dev *wpan_dev); #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL void (*get_llsec_table)(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev, @@ -314,6 +319,24 @@ struct cfg802154_scan_request { struct wpan_phy *wpan_phy; }; +/** + * struct cfg802154_beacon_request - Beacon request descriptor + * + * @interval: interval n between sendings, in multiple order of the super frame + * duration: aBaseSuperframeDuration * (2^n) unless the interval + * order is greater or equal to 15, in this case beacons won't be + * passively sent out at a fixed rate but instead inform the device + * that it should answer beacon requests as part of active scan + * procedures + * @wpan_dev: the concerned wpan device + * @wpan_phy: the wpan phy this was for + */ +struct cfg802154_beacon_request { + u8 interval; + struct wpan_dev *wpan_dev; + struct wpan_phy *wpan_phy; +}; + /** * struct cfg802154_mac_pkt - MAC packet descriptor (beacon/command) * @node: MAC packets to process list member diff --git a/include/net/nl802154.h b/include/net/nl802154.h index c267fa1c5aac..8cd9d141f5af 100644 --- a/include/net/nl802154.h +++ b/include/net/nl802154.h @@ -76,6 +76,8 @@ enum nl802154_commands { NL802154_CMD_TRIGGER_SCAN, NL802154_CMD_ABORT_SCAN, NL802154_CMD_SCAN_DONE, + NL802154_CMD_SEND_BEACONS, + NL802154_CMD_STOP_BEACONS, /* add new commands above here */ @@ -144,6 +146,7 @@ enum nl802154_attrs { NL802154_ATTR_SCAN_MEAN_PRF, NL802154_ATTR_SCAN_DURATION, NL802154_ATTR_SCAN_DONE_REASON, + NL802154_ATTR_BEACON_INTERVAL, /* add attributes here, update the policy in nl802154.c */ diff --git a/net/ieee802154/nl802154.c b/net/ieee802154/nl802154.c index 0b7a9f16b3b6..8661907599e1 100644 --- a/net/ieee802154/nl802154.c +++ b/net/ieee802154/nl802154.c @@ -227,6 +227,7 @@ static const struct nla_policy nl802154_policy[NL802154_ATTR_MAX+1] = { [NL802154_ATTR_SCAN_MEAN_PRF] = { .type = NLA_U8 }, [NL802154_ATTR_SCAN_DURATION] = { .type = NLA_U8 }, [NL802154_ATTR_SCAN_DONE_REASON] = { .type = NLA_U8 }, + [NL802154_ATTR_BEACON_INTERVAL] = { .type = NLA_U8 }, #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL [NL802154_ATTR_SEC_ENABLED] = { .type = NLA_U8, }, @@ -1587,6 +1588,82 @@ static int nl802154_abort_scan(struct sk_buff *skb, struct genl_info *info) return rdev_abort_scan(rdev, wpan_dev); } +static int +nl802154_send_beacons(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg802154_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct wpan_dev *wpan_dev = dev->ieee802154_ptr; + struct wpan_phy *wpan_phy = &rdev->wpan_phy; + struct cfg802154_beacon_request *request; + int err; + + /* Only coordinators can send beacons */ + if (wpan_dev->iftype != NL802154_IFTYPE_COORD) + return -EOPNOTSUPP; + + if (wpan_dev->pan_id == cpu_to_le16(IEEE802154_PANID_BROADCAST)) { + pr_err("Device is not part of any PAN\n"); + return -EPERM; + } + + request = kzalloc(sizeof(*request), GFP_KERNEL); + if (!request) + return -ENOMEM; + + request->wpan_dev = wpan_dev; + request->wpan_phy = wpan_phy; + + if (info->attrs[NL802154_ATTR_BEACON_INTERVAL]) { + request->interval = nla_get_u8(info->attrs[NL802154_ATTR_BEACON_INTERVAL]); + if (request->interval > IEEE802154_MAX_SCAN_DURATION) { + pr_err("Interval is out of range\n"); + err = -EINVAL; + goto free_request; + } + } else { + /* Use maximum duration order by default */ + request->interval = IEEE802154_MAX_SCAN_DURATION; + } + + if (wpan_dev->netdev) + dev_hold(wpan_dev->netdev); + + err = rdev_send_beacons(rdev, request); + if (err) { + pr_err("Failure starting sending beacons (%d)\n", err); + goto free_device; + } + + return 0; + +free_device: + if (wpan_dev->netdev) + dev_put(wpan_dev->netdev); +free_request: + kfree(request); + + return err; +} + +void nl802154_beaconing_done(struct wpan_dev *wpan_dev) +{ + if (wpan_dev->netdev) + dev_put(wpan_dev->netdev); +} +EXPORT_SYMBOL_GPL(nl802154_beaconing_done); + +static int +nl802154_stop_beacons(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg802154_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct wpan_dev *wpan_dev = dev->ieee802154_ptr; + + /* Resources are released in the notification helper above */ + return rdev_stop_beacons(rdev, wpan_dev); +} + #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL static const struct nla_policy nl802154_dev_addr_policy[NL802154_DEV_ADDR_ATTR_MAX + 1] = { [NL802154_DEV_ADDR_ATTR_PAN_ID] = { .type = NLA_U16 }, @@ -2693,6 +2770,22 @@ static const struct genl_ops nl802154_ops[] = { NL802154_FLAG_CHECK_NETDEV_UP | NL802154_FLAG_NEED_RTNL, }, + { + .cmd = NL802154_CMD_SEND_BEACONS, + .doit = nl802154_send_beacons, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL802154_FLAG_NEED_NETDEV | + NL802154_FLAG_CHECK_NETDEV_UP | + NL802154_FLAG_NEED_RTNL, + }, + { + .cmd = NL802154_CMD_STOP_BEACONS, + .doit = nl802154_stop_beacons, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL802154_FLAG_NEED_NETDEV | + NL802154_FLAG_CHECK_NETDEV_UP | + NL802154_FLAG_NEED_RTNL, + }, #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL { .cmd = NL802154_CMD_SET_SEC_PARAMS, diff --git a/net/ieee802154/nl802154.h b/net/ieee802154/nl802154.h index cfa7134be747..d69d950f9a6a 100644 --- a/net/ieee802154/nl802154.h +++ b/net/ieee802154/nl802154.h @@ -9,5 +9,6 @@ int nl802154_scan_event(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev, int nl802154_scan_started(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev); int nl802154_scan_done(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev, enum nl802154_scan_done_reasons reason); +void nl802154_beaconing_done(struct wpan_dev *wpan_dev); #endif /* __IEEE802154_NL802154_H */ diff --git a/net/ieee802154/rdev-ops.h b/net/ieee802154/rdev-ops.h index e171d74c3251..5eaae15c610e 100644 --- a/net/ieee802154/rdev-ops.h +++ b/net/ieee802154/rdev-ops.h @@ -237,6 +237,34 @@ static inline int rdev_abort_scan(struct cfg802154_registered_device *rdev, return ret; } +static inline int rdev_send_beacons(struct cfg802154_registered_device *rdev, + struct cfg802154_beacon_request *request) +{ + int ret; + + if (!rdev->ops->send_beacons) + return -EOPNOTSUPP; + + trace_802154_rdev_send_beacons(&rdev->wpan_phy, request); + ret = rdev->ops->send_beacons(&rdev->wpan_phy, request); + trace_802154_rdev_return_int(&rdev->wpan_phy, ret); + return ret; +} + +static inline int rdev_stop_beacons(struct cfg802154_registered_device *rdev, + struct wpan_dev *wpan_dev) +{ + int ret; + + if (!rdev->ops->stop_beacons) + return -EOPNOTSUPP; + + trace_802154_rdev_stop_beacons(&rdev->wpan_phy, wpan_dev); + ret = rdev->ops->stop_beacons(&rdev->wpan_phy, wpan_dev); + trace_802154_rdev_return_int(&rdev->wpan_phy, ret); + return ret; +} + #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL /* TODO this is already a nl802154, so move into ieee802154 */ static inline void diff --git a/net/ieee802154/trace.h b/net/ieee802154/trace.h index e5405f737ded..e5d8439b9e45 100644 --- a/net/ieee802154/trace.h +++ b/net/ieee802154/trace.h @@ -315,6 +315,22 @@ TRACE_EVENT(802154_rdev_trigger_scan, WPAN_PHY_PR_ARG, __entry->page, __entry->channels, __entry->duration) ); +TRACE_EVENT(802154_rdev_send_beacons, + TP_PROTO(struct wpan_phy *wpan_phy, + struct cfg802154_beacon_request *request), + TP_ARGS(wpan_phy, request), + TP_STRUCT__entry( + WPAN_PHY_ENTRY + __field(u8, interval) + ), + TP_fast_assign( + WPAN_PHY_ASSIGN; + __entry->interval = request->interval; + ), + TP_printk(WPAN_PHY_PR_FMT ", sending beacons (interval order: %d)", + WPAN_PHY_PR_ARG, __entry->interval) +); + DECLARE_EVENT_CLASS(802154_wdev_template, TP_PROTO(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev), TP_ARGS(wpan_phy, wpan_dev), @@ -335,6 +351,11 @@ DEFINE_EVENT(802154_wdev_template, 802154_rdev_abort_scan, TP_ARGS(wpan_phy, wpan_dev) ); +DEFINE_EVENT(802154_wdev_template, 802154_rdev_stop_beacons, + TP_PROTO(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev), + TP_ARGS(wpan_phy, wpan_dev) +); + TRACE_EVENT(802154_rdev_return_int, TP_PROTO(struct wpan_phy *wpan_phy, int ret), TP_ARGS(wpan_phy, ret), From 3accf4762734a69ebd03cba989249c78ac7dfc7e Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 25 Jan 2023 11:29:23 +0100 Subject: [PATCH 08/20] mac802154: Handle basic beaconing 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 Signed-off-by: David Girault Signed-off-by: Miquel Raynal Acked-by: Alexander Aring Link: https://lore.kernel.org/r/20230125102923.135465-3-miquel.raynal@bootlin.com Signed-off-by: Stefan Schmidt --- include/net/ieee802154_netdev.h | 16 ++++ net/ieee802154/header_ops.c | 24 +++++ net/mac802154/cfg.c | 31 ++++++- net/mac802154/ieee802154_i.h | 18 ++++ net/mac802154/iface.c | 3 + net/mac802154/llsec.c | 5 +- net/mac802154/main.c | 1 + net/mac802154/scan.c | 151 ++++++++++++++++++++++++++++++++ 8 files changed, 246 insertions(+), 3 deletions(-) diff --git a/include/net/ieee802154_netdev.h b/include/net/ieee802154_netdev.h index 2f2196049a86..da8a3e648c7a 100644 --- a/include/net/ieee802154_netdev.h +++ b/include/net/ieee802154_netdev.h @@ -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 diff --git a/net/ieee802154/header_ops.c b/net/ieee802154/header_ops.c index af337cf62764..35d384dfe29d 100644 --- a/net/ieee802154/header_ops.c +++ b/net/ieee802154/header_ops.c @@ -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) diff --git a/net/mac802154/cfg.c b/net/mac802154/cfg.c index 187cebcaf233..5c3cb019f751 100644 --- a/net/mac802154/cfg.c +++ b/net/mac802154/cfg.c @@ -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, diff --git a/net/mac802154/ieee802154_i.h b/net/mac802154/ieee802154_i.h index 0e4db967bd1d..63bab99ed368 100644 --- a/net/mac802154/ieee802154_i.h +++ b/net/mac802154/ieee802154_i.h @@ -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); diff --git a/net/mac802154/iface.c b/net/mac802154/iface.c index a5958d323ea3..9d59caeb74e0 100644 --- a/net/mac802154/iface.c +++ b/net/mac802154/iface.c @@ -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--; diff --git a/net/mac802154/llsec.c b/net/mac802154/llsec.c index 55550ead2ced..8d2eabc71bbe 100644 --- a/net/mac802154/llsec.c +++ b/net/mac802154/llsec.c @@ -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 || diff --git a/net/mac802154/main.c b/net/mac802154/main.c index b1111279e06d..ee23e234b998 100644 --- a/net/mac802154/main.c +++ b/net/mac802154/main.c @@ -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; diff --git a/net/mac802154/scan.c b/net/mac802154/scan.c index 56056b9c93c1..cfbe20b1ec5e 100644 --- a/net/mac802154/scan.c +++ b/net/mac802154/scan.c @@ -16,6 +16,11 @@ #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; +} From 622bd6ea90086beb98ac439edd7d57de73d1d6f9 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 26 Jan 2023 17:22:59 +0100 Subject: [PATCH 09/20] at86rf230: convert to gpio descriptors There are no remaining in-tree users of the platform_data, so this driver can be converted to using the simpler gpiod interfaces. Any out-of-tree users that rely on the platform data can provide the data using the device_property and gpio_lookup interfaces instead. Reviewed-by: Miquel Raynal Reviewed-by: Andy Shevchenko Signed-off-by: Arnd Bergmann Link: https://lore.kernel.org/r/20230126162323.2986682-1-arnd@kernel.org Signed-off-by: Stefan Schmidt --- drivers/net/ieee802154/at86rf230.c | 82 +++++++++--------------------- include/linux/spi/at86rf230.h | 20 -------- 2 files changed, 25 insertions(+), 77 deletions(-) delete mode 100644 include/linux/spi/at86rf230.h diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c index 15f283b26721..a4a6b7490fdc 100644 --- a/drivers/net/ieee802154/at86rf230.c +++ b/drivers/net/ieee802154/at86rf230.c @@ -15,13 +15,12 @@ #include #include #include -#include #include +#include #include -#include +#include #include #include -#include #include #include @@ -82,7 +81,7 @@ struct at86rf230_local { struct ieee802154_hw *hw; struct at86rf2xx_chip_data *data; struct regmap *regmap; - int slp_tr; + struct gpio_desc *slp_tr; bool sleep; struct completion state_complete; @@ -107,8 +106,8 @@ at86rf230_async_state_change(struct at86rf230_local *lp, static inline void at86rf230_sleep(struct at86rf230_local *lp) { - if (gpio_is_valid(lp->slp_tr)) { - gpio_set_value(lp->slp_tr, 1); + if (lp->slp_tr) { + gpiod_set_value(lp->slp_tr, 1); usleep_range(lp->data->t_off_to_sleep, lp->data->t_off_to_sleep + 10); lp->sleep = true; @@ -118,8 +117,8 @@ at86rf230_sleep(struct at86rf230_local *lp) static inline void at86rf230_awake(struct at86rf230_local *lp) { - if (gpio_is_valid(lp->slp_tr)) { - gpio_set_value(lp->slp_tr, 0); + if (lp->slp_tr) { + gpiod_set_value(lp->slp_tr, 0); usleep_range(lp->data->t_sleep_to_off, lp->data->t_sleep_to_off + 100); lp->sleep = false; @@ -204,9 +203,9 @@ at86rf230_write_subreg(struct at86rf230_local *lp, static inline void at86rf230_slp_tr_rising_edge(struct at86rf230_local *lp) { - gpio_set_value(lp->slp_tr, 1); + gpiod_set_value(lp->slp_tr, 1); udelay(1); - gpio_set_value(lp->slp_tr, 0); + gpiod_set_value(lp->slp_tr, 0); } static bool @@ -819,7 +818,7 @@ at86rf230_write_frame_complete(void *context) ctx->trx.len = 2; - if (gpio_is_valid(lp->slp_tr)) + if (lp->slp_tr) at86rf230_slp_tr_rising_edge(lp); else at86rf230_async_write_reg(lp, RG_TRX_STATE, STATE_BUSY_TX, ctx, @@ -1415,32 +1414,6 @@ static int at86rf230_hw_init(struct at86rf230_local *lp, u8 xtal_trim) return at86rf230_write_subreg(lp, SR_SLOTTED_OPERATION, 0); } -static int -at86rf230_get_pdata(struct spi_device *spi, int *rstn, int *slp_tr, - u8 *xtal_trim) -{ - struct at86rf230_platform_data *pdata = spi->dev.platform_data; - int ret; - - if (!IS_ENABLED(CONFIG_OF) || !spi->dev.of_node) { - if (!pdata) - return -ENOENT; - - *rstn = pdata->rstn; - *slp_tr = pdata->slp_tr; - *xtal_trim = pdata->xtal_trim; - return 0; - } - - *rstn = of_get_named_gpio(spi->dev.of_node, "reset-gpio", 0); - *slp_tr = of_get_named_gpio(spi->dev.of_node, "sleep-gpio", 0); - ret = of_property_read_u8(spi->dev.of_node, "xtal-trim", xtal_trim); - if (ret < 0 && ret != -EINVAL) - return ret; - - return 0; -} - static int at86rf230_detect_device(struct at86rf230_local *lp) { @@ -1547,7 +1520,8 @@ static int at86rf230_probe(struct spi_device *spi) struct ieee802154_hw *hw; struct at86rf230_local *lp; unsigned int status; - int rc, irq_type, rstn, slp_tr; + int rc, irq_type; + struct gpio_desc *rstn, *slp_tr; u8 xtal_trim = 0; if (!spi->irq) { @@ -1555,32 +1529,26 @@ static int at86rf230_probe(struct spi_device *spi) return -EINVAL; } - rc = at86rf230_get_pdata(spi, &rstn, &slp_tr, &xtal_trim); - if (rc < 0) { - dev_err(&spi->dev, "failed to parse platform_data: %d\n", rc); + rc = device_property_read_u8(&spi->dev, "xtal-trim", &xtal_trim); + if (rc < 0 && rc != -EINVAL) { + dev_err(&spi->dev, "failed to parse xtal-trim: %d\n", rc); return rc; } - if (gpio_is_valid(rstn)) { - rc = devm_gpio_request_one(&spi->dev, rstn, - GPIOF_OUT_INIT_HIGH, "rstn"); - if (rc) - return rc; - } + rstn = devm_gpiod_get_optional(&spi->dev, "rstn", GPIOD_OUT_HIGH); + if (IS_ERR(rstn)) + return PTR_ERR(rstn); - if (gpio_is_valid(slp_tr)) { - rc = devm_gpio_request_one(&spi->dev, slp_tr, - GPIOF_OUT_INIT_LOW, "slp_tr"); - if (rc) - return rc; - } + slp_tr = devm_gpiod_get_optional(&spi->dev, "slp_tr", GPIOD_OUT_LOW); + if (IS_ERR(slp_tr)) + return PTR_ERR(slp_tr); /* Reset */ - if (gpio_is_valid(rstn)) { + if (rstn) { udelay(1); - gpio_set_value_cansleep(rstn, 0); + gpiod_set_value_cansleep(rstn, 0); udelay(1); - gpio_set_value_cansleep(rstn, 1); + gpiod_set_value_cansleep(rstn, 1); usleep_range(120, 240); } @@ -1682,7 +1650,7 @@ MODULE_DEVICE_TABLE(spi, at86rf230_device_id); static struct spi_driver at86rf230_driver = { .id_table = at86rf230_device_id, .driver = { - .of_match_table = of_match_ptr(at86rf230_of_match), + .of_match_table = at86rf230_of_match, .name = "at86rf230", }, .probe = at86rf230_probe, diff --git a/include/linux/spi/at86rf230.h b/include/linux/spi/at86rf230.h deleted file mode 100644 index d278576ab692..000000000000 --- a/include/linux/spi/at86rf230.h +++ /dev/null @@ -1,20 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * AT86RF230/RF231 driver - * - * Copyright (C) 2009-2012 Siemens AG - * - * Written by: - * Dmitry Eremin-Solenikov - */ -#ifndef AT86RF230_H -#define AT86RF230_H - -struct at86rf230_platform_data { - int rstn; - int slp_tr; - int dig2; - u8 xtal_trim; -}; - -#endif From 8338304c2719fb7aec8e4b5dd9fa684b719db06e Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Mon, 30 Jan 2023 16:43:06 +0100 Subject: [PATCH 10/20] mac802154: Avoid superfluous endianness handling When compiling scan.c with C=1, Sparse complains with: sparse: expected unsigned short [usertype] val sparse: got restricted __le16 [usertype] pan_id sparse: sparse: cast from restricted __le16 sparse: expected unsigned long long [usertype] val sparse: got restricted __le64 [usertype] extended_addr sparse: sparse: cast from restricted __le64 The tool is right, both pan_id and extended_addr already are rightfully defined as being __le16 and __le64 on both sides of the operations and do not require extra endianness handling. Fixes: 3accf4762734 ("mac802154: Handle basic beaconing") Reported-by: kernel test robot Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/r/20230130154306.114265-1-miquel.raynal@bootlin.com Signed-off-by: Stefan Schmidt --- net/mac802154/scan.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/mac802154/scan.c b/net/mac802154/scan.c index cfbe20b1ec5e..8f98efec7753 100644 --- a/net/mac802154/scan.c +++ b/net/mac802154/scan.c @@ -419,8 +419,8 @@ int mac802154_send_beacons_locked(struct ieee802154_sub_if_data *sdata, 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.mhr.source.pan_id = request->wpan_dev->pan_id; + local->beacon.mhr.source.extended_addr = 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; From 6755dee8343cbc4af45e001d904c9a857a451bec Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 26 Jan 2023 17:15:59 +0100 Subject: [PATCH 11/20] cc2520: move to gpio descriptors cc2520 supports both probing from static platform_data and from devicetree, but there have never been any definitions of the platform data in the mainline kernel, so it's safe to assume that only the DT path is used. After folding cc2520_platform_data into the driver itself, the GPIO handling can be simplified by moving to the modern gpiod interface. Signed-off-by: Arnd Bergmann Link: https://lore.kernel.org/r/20230126161658.2983292-1-arnd@kernel.org Signed-off-by: Stefan Schmidt --- MAINTAINERS | 1 - drivers/net/ieee802154/cc2520.c | 136 +++++++++----------------------- include/linux/spi/cc2520.h | 21 ----- 3 files changed, 37 insertions(+), 121 deletions(-) delete mode 100644 include/linux/spi/cc2520.h diff --git a/MAINTAINERS b/MAINTAINERS index 955c1be1efb2..6fc990f36d63 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4743,7 +4743,6 @@ L: linux-wpan@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/net/ieee802154/cc2520.txt F: drivers/net/ieee802154/cc2520.c -F: include/linux/spi/cc2520.h CCREE ARM TRUSTZONE CRYPTOCELL REE DRIVER M: Gilad Ben-Yossef diff --git a/drivers/net/ieee802154/cc2520.c b/drivers/net/ieee802154/cc2520.c index c69b87d3837d..7272fba55fe8 100644 --- a/drivers/net/ieee802154/cc2520.c +++ b/drivers/net/ieee802154/cc2520.c @@ -7,14 +7,13 @@ */ #include #include -#include +#include #include #include -#include +#include #include #include #include -#include #include #include #include @@ -206,7 +205,7 @@ struct cc2520_private { struct mutex buffer_mutex; /* SPI buffer mutex */ bool is_tx; /* Flag for sync b/w Tx and Rx */ bool amplified; /* Flag for CC2591 */ - int fifo_pin; /* FIFO GPIO pin number */ + struct gpio_desc *fifo_pin; /* FIFO GPIO pin number */ struct work_struct fifop_irqwork;/* Workqueue for FIFOP */ spinlock_t lock; /* Lock for is_tx*/ struct completion tx_complete; /* Work completion for Tx */ @@ -875,7 +874,7 @@ static void cc2520_fifop_irqwork(struct work_struct *work) dev_dbg(&priv->spi->dev, "fifop interrupt received\n"); - if (gpio_get_value(priv->fifo_pin)) + if (gpiod_get_value(priv->fifo_pin)) cc2520_rx(priv); else dev_dbg(&priv->spi->dev, "rxfifo overflow\n"); @@ -912,49 +911,11 @@ static irqreturn_t cc2520_sfd_isr(int irq, void *data) return IRQ_HANDLED; } -static int cc2520_get_platform_data(struct spi_device *spi, - struct cc2520_platform_data *pdata) -{ - struct device_node *np = spi->dev.of_node; - struct cc2520_private *priv = spi_get_drvdata(spi); - - if (!np) { - struct cc2520_platform_data *spi_pdata = spi->dev.platform_data; - - if (!spi_pdata) - return -ENOENT; - *pdata = *spi_pdata; - priv->fifo_pin = pdata->fifo; - return 0; - } - - pdata->fifo = of_get_named_gpio(np, "fifo-gpio", 0); - priv->fifo_pin = pdata->fifo; - - pdata->fifop = of_get_named_gpio(np, "fifop-gpio", 0); - - pdata->sfd = of_get_named_gpio(np, "sfd-gpio", 0); - pdata->cca = of_get_named_gpio(np, "cca-gpio", 0); - pdata->vreg = of_get_named_gpio(np, "vreg-gpio", 0); - pdata->reset = of_get_named_gpio(np, "reset-gpio", 0); - - /* CC2591 front end for CC2520 */ - if (of_property_read_bool(np, "amplified")) - priv->amplified = true; - - return 0; -} - static int cc2520_hw_init(struct cc2520_private *priv) { u8 status = 0, state = 0xff; int ret; int timeout = 100; - struct cc2520_platform_data pdata; - - ret = cc2520_get_platform_data(priv->spi, &pdata); - if (ret) - goto err_ret; ret = cc2520_read_register(priv, CC2520_FSMSTAT1, &state); if (ret) @@ -1071,7 +1032,11 @@ err_ret: static int cc2520_probe(struct spi_device *spi) { struct cc2520_private *priv; - struct cc2520_platform_data pdata; + struct gpio_desc *fifop; + struct gpio_desc *cca; + struct gpio_desc *sfd; + struct gpio_desc *reset; + struct gpio_desc *vreg; int ret; priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL); @@ -1080,11 +1045,11 @@ static int cc2520_probe(struct spi_device *spi) spi_set_drvdata(spi, priv); - ret = cc2520_get_platform_data(spi, &pdata); - if (ret < 0) { - dev_err(&spi->dev, "no platform data\n"); - return -EINVAL; - } + /* CC2591 front end for CC2520 */ + /* Assumption that CC2591 is not connected */ + priv->amplified = false; + if (device_property_read_bool(&spi->dev, "amplified")) + priv->amplified = true; priv->spi = spi; @@ -1098,80 +1063,53 @@ static int cc2520_probe(struct spi_device *spi) spin_lock_init(&priv->lock); init_completion(&priv->tx_complete); - /* Assumption that CC2591 is not connected */ - priv->amplified = false; - /* Request all the gpio's */ - if (!gpio_is_valid(pdata.fifo)) { + priv->fifo_pin = devm_gpiod_get(&spi->dev, "fifo", GPIOD_IN); + if (IS_ERR(priv->fifo_pin)) { dev_err(&spi->dev, "fifo gpio is not valid\n"); - ret = -EINVAL; + ret = PTR_ERR(priv->fifo_pin); goto err_hw_init; } - ret = devm_gpio_request_one(&spi->dev, pdata.fifo, - GPIOF_IN, "fifo"); - if (ret) - goto err_hw_init; - - if (!gpio_is_valid(pdata.cca)) { + cca = devm_gpiod_get(&spi->dev, "cca", GPIOD_IN); + if (IS_ERR(cca)) { dev_err(&spi->dev, "cca gpio is not valid\n"); - ret = -EINVAL; + ret = PTR_ERR(cca); goto err_hw_init; } - ret = devm_gpio_request_one(&spi->dev, pdata.cca, - GPIOF_IN, "cca"); - if (ret) - goto err_hw_init; - - if (!gpio_is_valid(pdata.fifop)) { + fifop = devm_gpiod_get(&spi->dev, "fifop", GPIOD_IN); + if (IS_ERR(fifop)) { dev_err(&spi->dev, "fifop gpio is not valid\n"); - ret = -EINVAL; + ret = PTR_ERR(fifop); goto err_hw_init; } - ret = devm_gpio_request_one(&spi->dev, pdata.fifop, - GPIOF_IN, "fifop"); - if (ret) - goto err_hw_init; - - if (!gpio_is_valid(pdata.sfd)) { + sfd = devm_gpiod_get(&spi->dev, "sfd", GPIOD_IN); + if (IS_ERR(sfd)) { dev_err(&spi->dev, "sfd gpio is not valid\n"); - ret = -EINVAL; + ret = PTR_ERR(sfd); goto err_hw_init; } - ret = devm_gpio_request_one(&spi->dev, pdata.sfd, - GPIOF_IN, "sfd"); - if (ret) - goto err_hw_init; - - if (!gpio_is_valid(pdata.reset)) { + reset = devm_gpiod_get(&spi->dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(reset)) { dev_err(&spi->dev, "reset gpio is not valid\n"); - ret = -EINVAL; + ret = PTR_ERR(reset); goto err_hw_init; } - ret = devm_gpio_request_one(&spi->dev, pdata.reset, - GPIOF_OUT_INIT_LOW, "reset"); - if (ret) - goto err_hw_init; - - if (!gpio_is_valid(pdata.vreg)) { + vreg = devm_gpiod_get(&spi->dev, "vreg", GPIOD_OUT_LOW); + if (IS_ERR(vreg)) { dev_err(&spi->dev, "vreg gpio is not valid\n"); - ret = -EINVAL; + ret = PTR_ERR(vreg); goto err_hw_init; } - ret = devm_gpio_request_one(&spi->dev, pdata.vreg, - GPIOF_OUT_INIT_LOW, "vreg"); - if (ret) - goto err_hw_init; - - gpio_set_value(pdata.vreg, HIGH); + gpiod_set_value(vreg, HIGH); usleep_range(100, 150); - gpio_set_value(pdata.reset, HIGH); + gpiod_set_value(reset, HIGH); usleep_range(200, 250); ret = cc2520_hw_init(priv); @@ -1180,7 +1118,7 @@ static int cc2520_probe(struct spi_device *spi) /* Set up fifop interrupt */ ret = devm_request_irq(&spi->dev, - gpio_to_irq(pdata.fifop), + gpiod_to_irq(fifop), cc2520_fifop_isr, IRQF_TRIGGER_RISING, dev_name(&spi->dev), @@ -1192,7 +1130,7 @@ static int cc2520_probe(struct spi_device *spi) /* Set up sfd interrupt */ ret = devm_request_irq(&spi->dev, - gpio_to_irq(pdata.sfd), + gpiod_to_irq(sfd), cc2520_sfd_isr, IRQF_TRIGGER_FALLING, dev_name(&spi->dev), @@ -1241,7 +1179,7 @@ MODULE_DEVICE_TABLE(of, cc2520_of_ids); static struct spi_driver cc2520_driver = { .driver = { .name = "cc2520", - .of_match_table = of_match_ptr(cc2520_of_ids), + .of_match_table = cc2520_of_ids, }, .id_table = cc2520_ids, .probe = cc2520_probe, diff --git a/include/linux/spi/cc2520.h b/include/linux/spi/cc2520.h deleted file mode 100644 index 449bacf10700..000000000000 --- a/include/linux/spi/cc2520.h +++ /dev/null @@ -1,21 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* Header file for cc2520 radio driver - * - * Copyright (C) 2014 Varka Bhadram - * Md.Jamal Mohiuddin - * P Sowjanya - */ - -#ifndef __CC2520_H -#define __CC2520_H - -struct cc2520_platform_data { - int fifo; - int fifop; - int cca; - int sfd; - int reset; - int vreg; -}; - -#endif From 9f2ad955f98366003e887e32bdd4ea98ef2a04f4 Mon Sep 17 00:00:00 2001 From: Stefan Schmidt Date: Wed, 1 Feb 2023 21:10:26 +0100 Subject: [PATCH 12/20] Revert "at86rf230: convert to gpio descriptors" This reverts commit 622bd6ea90086beb98ac439edd7d57de73d1d6f9. Dmitry Torokhov points out that this conversion leaves an existing board in reset state due to not properly handled polarity. Additionally, the GPIO name inadvertenly changes from "reset-gpio" to "rstn-gpios". Revert to avoid these regressions. Follow up patches for a better conversion are applied as well. Signed-off-by: Stefan Schmidt --- drivers/net/ieee802154/at86rf230.c | 82 +++++++++++++++++++++--------- include/linux/spi/at86rf230.h | 20 ++++++++ 2 files changed, 77 insertions(+), 25 deletions(-) create mode 100644 include/linux/spi/at86rf230.h diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c index a4a6b7490fdc..15f283b26721 100644 --- a/drivers/net/ieee802154/at86rf230.c +++ b/drivers/net/ieee802154/at86rf230.c @@ -15,12 +15,13 @@ #include #include #include +#include #include -#include #include -#include +#include #include #include +#include #include #include @@ -81,7 +82,7 @@ struct at86rf230_local { struct ieee802154_hw *hw; struct at86rf2xx_chip_data *data; struct regmap *regmap; - struct gpio_desc *slp_tr; + int slp_tr; bool sleep; struct completion state_complete; @@ -106,8 +107,8 @@ at86rf230_async_state_change(struct at86rf230_local *lp, static inline void at86rf230_sleep(struct at86rf230_local *lp) { - if (lp->slp_tr) { - gpiod_set_value(lp->slp_tr, 1); + if (gpio_is_valid(lp->slp_tr)) { + gpio_set_value(lp->slp_tr, 1); usleep_range(lp->data->t_off_to_sleep, lp->data->t_off_to_sleep + 10); lp->sleep = true; @@ -117,8 +118,8 @@ at86rf230_sleep(struct at86rf230_local *lp) static inline void at86rf230_awake(struct at86rf230_local *lp) { - if (lp->slp_tr) { - gpiod_set_value(lp->slp_tr, 0); + if (gpio_is_valid(lp->slp_tr)) { + gpio_set_value(lp->slp_tr, 0); usleep_range(lp->data->t_sleep_to_off, lp->data->t_sleep_to_off + 100); lp->sleep = false; @@ -203,9 +204,9 @@ at86rf230_write_subreg(struct at86rf230_local *lp, static inline void at86rf230_slp_tr_rising_edge(struct at86rf230_local *lp) { - gpiod_set_value(lp->slp_tr, 1); + gpio_set_value(lp->slp_tr, 1); udelay(1); - gpiod_set_value(lp->slp_tr, 0); + gpio_set_value(lp->slp_tr, 0); } static bool @@ -818,7 +819,7 @@ at86rf230_write_frame_complete(void *context) ctx->trx.len = 2; - if (lp->slp_tr) + if (gpio_is_valid(lp->slp_tr)) at86rf230_slp_tr_rising_edge(lp); else at86rf230_async_write_reg(lp, RG_TRX_STATE, STATE_BUSY_TX, ctx, @@ -1414,6 +1415,32 @@ static int at86rf230_hw_init(struct at86rf230_local *lp, u8 xtal_trim) return at86rf230_write_subreg(lp, SR_SLOTTED_OPERATION, 0); } +static int +at86rf230_get_pdata(struct spi_device *spi, int *rstn, int *slp_tr, + u8 *xtal_trim) +{ + struct at86rf230_platform_data *pdata = spi->dev.platform_data; + int ret; + + if (!IS_ENABLED(CONFIG_OF) || !spi->dev.of_node) { + if (!pdata) + return -ENOENT; + + *rstn = pdata->rstn; + *slp_tr = pdata->slp_tr; + *xtal_trim = pdata->xtal_trim; + return 0; + } + + *rstn = of_get_named_gpio(spi->dev.of_node, "reset-gpio", 0); + *slp_tr = of_get_named_gpio(spi->dev.of_node, "sleep-gpio", 0); + ret = of_property_read_u8(spi->dev.of_node, "xtal-trim", xtal_trim); + if (ret < 0 && ret != -EINVAL) + return ret; + + return 0; +} + static int at86rf230_detect_device(struct at86rf230_local *lp) { @@ -1520,8 +1547,7 @@ static int at86rf230_probe(struct spi_device *spi) struct ieee802154_hw *hw; struct at86rf230_local *lp; unsigned int status; - int rc, irq_type; - struct gpio_desc *rstn, *slp_tr; + int rc, irq_type, rstn, slp_tr; u8 xtal_trim = 0; if (!spi->irq) { @@ -1529,26 +1555,32 @@ static int at86rf230_probe(struct spi_device *spi) return -EINVAL; } - rc = device_property_read_u8(&spi->dev, "xtal-trim", &xtal_trim); - if (rc < 0 && rc != -EINVAL) { - dev_err(&spi->dev, "failed to parse xtal-trim: %d\n", rc); + rc = at86rf230_get_pdata(spi, &rstn, &slp_tr, &xtal_trim); + if (rc < 0) { + dev_err(&spi->dev, "failed to parse platform_data: %d\n", rc); return rc; } - rstn = devm_gpiod_get_optional(&spi->dev, "rstn", GPIOD_OUT_HIGH); - if (IS_ERR(rstn)) - return PTR_ERR(rstn); + if (gpio_is_valid(rstn)) { + rc = devm_gpio_request_one(&spi->dev, rstn, + GPIOF_OUT_INIT_HIGH, "rstn"); + if (rc) + return rc; + } - slp_tr = devm_gpiod_get_optional(&spi->dev, "slp_tr", GPIOD_OUT_LOW); - if (IS_ERR(slp_tr)) - return PTR_ERR(slp_tr); + if (gpio_is_valid(slp_tr)) { + rc = devm_gpio_request_one(&spi->dev, slp_tr, + GPIOF_OUT_INIT_LOW, "slp_tr"); + if (rc) + return rc; + } /* Reset */ - if (rstn) { + if (gpio_is_valid(rstn)) { udelay(1); - gpiod_set_value_cansleep(rstn, 0); + gpio_set_value_cansleep(rstn, 0); udelay(1); - gpiod_set_value_cansleep(rstn, 1); + gpio_set_value_cansleep(rstn, 1); usleep_range(120, 240); } @@ -1650,7 +1682,7 @@ MODULE_DEVICE_TABLE(spi, at86rf230_device_id); static struct spi_driver at86rf230_driver = { .id_table = at86rf230_device_id, .driver = { - .of_match_table = at86rf230_of_match, + .of_match_table = of_match_ptr(at86rf230_of_match), .name = "at86rf230", }, .probe = at86rf230_probe, diff --git a/include/linux/spi/at86rf230.h b/include/linux/spi/at86rf230.h new file mode 100644 index 000000000000..d278576ab692 --- /dev/null +++ b/include/linux/spi/at86rf230.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * AT86RF230/RF231 driver + * + * Copyright (C) 2009-2012 Siemens AG + * + * Written by: + * Dmitry Eremin-Solenikov + */ +#ifndef AT86RF230_H +#define AT86RF230_H + +struct at86rf230_platform_data { + int rstn; + int slp_tr; + int dig2; + u8 xtal_trim; +}; + +#endif From 9b26ed18545056794bf9d3f3defd9e09719ff4f5 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 31 Jan 2023 21:34:46 -0800 Subject: [PATCH 13/20] ieee802154: at86rf230: drop support for platform data There are no users of platform data in the mainline tree, and new boards should use either ACPI or device tree, so let's stop supporting it. This will help with converting the driver to gpiod API. Signed-off-by: Dmitry Torokhov Link: https://lore.kernel.org/r/20230201053447.4098486-1-dmitry.torokhov@gmail.com Signed-off-by: Stefan Schmidt --- drivers/net/ieee802154/at86rf230.c | 42 ++++++++---------------------- include/linux/spi/at86rf230.h | 20 -------------- 2 files changed, 11 insertions(+), 51 deletions(-) delete mode 100644 include/linux/spi/at86rf230.h diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c index 15f283b26721..d6b6b355348b 100644 --- a/drivers/net/ieee802154/at86rf230.c +++ b/drivers/net/ieee802154/at86rf230.c @@ -17,8 +17,8 @@ #include #include #include +#include #include -#include #include #include #include @@ -1415,32 +1415,6 @@ static int at86rf230_hw_init(struct at86rf230_local *lp, u8 xtal_trim) return at86rf230_write_subreg(lp, SR_SLOTTED_OPERATION, 0); } -static int -at86rf230_get_pdata(struct spi_device *spi, int *rstn, int *slp_tr, - u8 *xtal_trim) -{ - struct at86rf230_platform_data *pdata = spi->dev.platform_data; - int ret; - - if (!IS_ENABLED(CONFIG_OF) || !spi->dev.of_node) { - if (!pdata) - return -ENOENT; - - *rstn = pdata->rstn; - *slp_tr = pdata->slp_tr; - *xtal_trim = pdata->xtal_trim; - return 0; - } - - *rstn = of_get_named_gpio(spi->dev.of_node, "reset-gpio", 0); - *slp_tr = of_get_named_gpio(spi->dev.of_node, "sleep-gpio", 0); - ret = of_property_read_u8(spi->dev.of_node, "xtal-trim", xtal_trim); - if (ret < 0 && ret != -EINVAL) - return ret; - - return 0; -} - static int at86rf230_detect_device(struct at86rf230_local *lp) { @@ -1548,19 +1522,24 @@ static int at86rf230_probe(struct spi_device *spi) struct at86rf230_local *lp; unsigned int status; int rc, irq_type, rstn, slp_tr; - u8 xtal_trim = 0; + u8 xtal_trim; if (!spi->irq) { dev_err(&spi->dev, "no IRQ specified\n"); return -EINVAL; } - rc = at86rf230_get_pdata(spi, &rstn, &slp_tr, &xtal_trim); + rc = device_property_read_u8(&spi->dev, "xtal-trim", &xtal_trim); if (rc < 0) { - dev_err(&spi->dev, "failed to parse platform_data: %d\n", rc); - return rc; + if (rc != -EINVAL) { + dev_err(&spi->dev, + "failed to parse xtal-trim: %d\n", rc); + return rc; + } + xtal_trim = 0; } + rstn = of_get_named_gpio(spi->dev.of_node, "reset-gpio", 0); if (gpio_is_valid(rstn)) { rc = devm_gpio_request_one(&spi->dev, rstn, GPIOF_OUT_INIT_HIGH, "rstn"); @@ -1568,6 +1547,7 @@ static int at86rf230_probe(struct spi_device *spi) return rc; } + slp_tr = of_get_named_gpio(spi->dev.of_node, "sleep-gpio", 0); if (gpio_is_valid(slp_tr)) { rc = devm_gpio_request_one(&spi->dev, slp_tr, GPIOF_OUT_INIT_LOW, "slp_tr"); diff --git a/include/linux/spi/at86rf230.h b/include/linux/spi/at86rf230.h deleted file mode 100644 index d278576ab692..000000000000 --- a/include/linux/spi/at86rf230.h +++ /dev/null @@ -1,20 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * AT86RF230/RF231 driver - * - * Copyright (C) 2009-2012 Siemens AG - * - * Written by: - * Dmitry Eremin-Solenikov - */ -#ifndef AT86RF230_H -#define AT86RF230_H - -struct at86rf230_platform_data { - int rstn; - int slp_tr; - int dig2; - u8 xtal_trim; -}; - -#endif From 6130543654e0e5a79485ed8538c0bf2259fe7431 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 31 Jan 2023 21:34:47 -0800 Subject: [PATCH 14/20] ieee802154: at86rf230: switch to using gpiod API Switch the driver from legacy gpio API that is deprecated to the newer gpiod API that respects line polarities described in ACPI/DT. Signed-off-by: Dmitry Torokhov Link: https://lore.kernel.org/r/20230201053447.4098486-2-dmitry.torokhov@gmail.com Signed-off-by: Stefan Schmidt --- drivers/net/ieee802154/at86rf230.c | 52 +++++++++++++++--------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c index d6b6b355348b..62b984f84d9f 100644 --- a/drivers/net/ieee802154/at86rf230.c +++ b/drivers/net/ieee802154/at86rf230.c @@ -82,7 +82,7 @@ struct at86rf230_local { struct ieee802154_hw *hw; struct at86rf2xx_chip_data *data; struct regmap *regmap; - int slp_tr; + struct gpio_desc *slp_tr; bool sleep; struct completion state_complete; @@ -107,8 +107,8 @@ at86rf230_async_state_change(struct at86rf230_local *lp, static inline void at86rf230_sleep(struct at86rf230_local *lp) { - if (gpio_is_valid(lp->slp_tr)) { - gpio_set_value(lp->slp_tr, 1); + if (lp->slp_tr) { + gpiod_set_value(lp->slp_tr, 1); usleep_range(lp->data->t_off_to_sleep, lp->data->t_off_to_sleep + 10); lp->sleep = true; @@ -118,8 +118,8 @@ at86rf230_sleep(struct at86rf230_local *lp) static inline void at86rf230_awake(struct at86rf230_local *lp) { - if (gpio_is_valid(lp->slp_tr)) { - gpio_set_value(lp->slp_tr, 0); + if (lp->slp_tr) { + gpiod_set_value(lp->slp_tr, 0); usleep_range(lp->data->t_sleep_to_off, lp->data->t_sleep_to_off + 100); lp->sleep = false; @@ -204,9 +204,9 @@ at86rf230_write_subreg(struct at86rf230_local *lp, static inline void at86rf230_slp_tr_rising_edge(struct at86rf230_local *lp) { - gpio_set_value(lp->slp_tr, 1); + gpiod_set_value(lp->slp_tr, 1); udelay(1); - gpio_set_value(lp->slp_tr, 0); + gpiod_set_value(lp->slp_tr, 0); } static bool @@ -819,7 +819,7 @@ at86rf230_write_frame_complete(void *context) ctx->trx.len = 2; - if (gpio_is_valid(lp->slp_tr)) + if (lp->slp_tr) at86rf230_slp_tr_rising_edge(lp); else at86rf230_async_write_reg(lp, RG_TRX_STATE, STATE_BUSY_TX, ctx, @@ -1520,8 +1520,10 @@ static int at86rf230_probe(struct spi_device *spi) { struct ieee802154_hw *hw; struct at86rf230_local *lp; + struct gpio_desc *slp_tr; + struct gpio_desc *rstn; unsigned int status; - int rc, irq_type, rstn, slp_tr; + int rc, irq_type; u8 xtal_trim; if (!spi->irq) { @@ -1539,28 +1541,26 @@ static int at86rf230_probe(struct spi_device *spi) xtal_trim = 0; } - rstn = of_get_named_gpio(spi->dev.of_node, "reset-gpio", 0); - if (gpio_is_valid(rstn)) { - rc = devm_gpio_request_one(&spi->dev, rstn, - GPIOF_OUT_INIT_HIGH, "rstn"); - if (rc) - return rc; - } + rstn = devm_gpiod_get_optional(&spi->dev, "reset", GPIOD_OUT_LOW); + rc = PTR_ERR_OR_ZERO(rstn); + if (rc) + return rc; - slp_tr = of_get_named_gpio(spi->dev.of_node, "sleep-gpio", 0); - if (gpio_is_valid(slp_tr)) { - rc = devm_gpio_request_one(&spi->dev, slp_tr, - GPIOF_OUT_INIT_LOW, "slp_tr"); - if (rc) - return rc; - } + gpiod_set_consumer_name(rstn, "rstn"); + + slp_tr = devm_gpiod_get_optional(&spi->dev, "sleep", GPIOD_OUT_LOW); + rc = PTR_ERR_OR_ZERO(slp_tr); + if (rc) + return rc; + + gpiod_set_consumer_name(slp_tr, "slp_tr"); /* Reset */ - if (gpio_is_valid(rstn)) { + if (rstn) { udelay(1); - gpio_set_value_cansleep(rstn, 0); + gpiod_set_value_cansleep(rstn, 1); udelay(1); - gpio_set_value_cansleep(rstn, 1); + gpiod_set_value_cansleep(rstn, 0); usleep_range(120, 240); } From 648324c9b690bcda0ac6f6499370effc0336ee27 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 14 Feb 2023 14:50:30 +0100 Subject: [PATCH 15/20] ieee802154: Use netlink policies when relevant on scan parameters Instead of open-coding scan parameters (page, channels, duration, etc), let's use the existing NLA_POLICY* macros. This help greatly reducing the error handling and clarifying the overall logic. Fixes: ed3557c947e1 ("ieee802154: Add support for user scanning requests") Suggested-by: Jakub Kicinski Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/r/20230214135035.1202471-2-miquel.raynal@bootlin.com Signed-off-by: Stefan Schmidt --- net/ieee802154/nl802154.c | 84 +++++++++++++-------------------------- 1 file changed, 28 insertions(+), 56 deletions(-) diff --git a/net/ieee802154/nl802154.c b/net/ieee802154/nl802154.c index 8661907599e1..3808a8e62a7b 100644 --- a/net/ieee802154/nl802154.c +++ b/net/ieee802154/nl802154.c @@ -187,8 +187,8 @@ static const struct nla_policy nl802154_policy[NL802154_ATTR_MAX+1] = { [NL802154_ATTR_WPAN_DEV] = { .type = NLA_U64 }, - [NL802154_ATTR_PAGE] = { .type = NLA_U8, }, - [NL802154_ATTR_CHANNEL] = { .type = NLA_U8, }, + [NL802154_ATTR_PAGE] = NLA_POLICY_MAX(NLA_U8, IEEE802154_MAX_PAGE), + [NL802154_ATTR_CHANNEL] = NLA_POLICY_MAX(NLA_U8, IEEE802154_MAX_CHANNEL), [NL802154_ATTR_TX_POWER] = { .type = NLA_S32, }, @@ -221,13 +221,19 @@ static const struct nla_policy nl802154_policy[NL802154_ATTR_MAX+1] = { [NL802154_ATTR_COORDINATOR] = { .type = NLA_NESTED }, - [NL802154_ATTR_SCAN_TYPE] = { .type = NLA_U8 }, - [NL802154_ATTR_SCAN_CHANNELS] = { .type = NLA_U32 }, - [NL802154_ATTR_SCAN_PREAMBLE_CODES] = { .type = NLA_U64 }, - [NL802154_ATTR_SCAN_MEAN_PRF] = { .type = NLA_U8 }, - [NL802154_ATTR_SCAN_DURATION] = { .type = NLA_U8 }, - [NL802154_ATTR_SCAN_DONE_REASON] = { .type = NLA_U8 }, - [NL802154_ATTR_BEACON_INTERVAL] = { .type = NLA_U8 }, + [NL802154_ATTR_SCAN_TYPE] = + NLA_POLICY_RANGE(NLA_U8, NL802154_SCAN_ED, NL802154_SCAN_RIT_PASSIVE), + [NL802154_ATTR_SCAN_CHANNELS] = + NLA_POLICY_MASK(NLA_U32, GENMASK(IEEE802154_MAX_CHANNEL, 0)), + [NL802154_ATTR_SCAN_PREAMBLE_CODES] = { .type = NLA_REJECT }, + [NL802154_ATTR_SCAN_MEAN_PRF] = { .type = NLA_REJECT }, + [NL802154_ATTR_SCAN_DURATION] = + NLA_POLICY_MAX(NLA_U8, IEEE802154_MAX_SCAN_DURATION), + [NL802154_ATTR_SCAN_DONE_REASON] = + NLA_POLICY_RANGE(NLA_U8, NL802154_SCAN_DONE_REASON_FINISHED, + NL802154_SCAN_DONE_REASON_ABORTED), + [NL802154_ATTR_BEACON_INTERVAL] = + NLA_POLICY_MAX(NLA_U8, IEEE802154_MAX_SCAN_DURATION), #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL [NL802154_ATTR_SEC_ENABLED] = { .type = NLA_U8, }, @@ -1423,51 +1429,23 @@ static int nl802154_trigger_scan(struct sk_buff *skb, struct genl_info *info) goto free_request; } - if (info->attrs[NL802154_ATTR_PAGE]) { + /* Use current page by default */ + if (info->attrs[NL802154_ATTR_PAGE]) request->page = nla_get_u8(info->attrs[NL802154_ATTR_PAGE]); - if (request->page > IEEE802154_MAX_PAGE) { - pr_err("Invalid page %d > %d\n", - request->page, IEEE802154_MAX_PAGE); - err = -EINVAL; - goto free_request; - } - } else { - /* Use current page by default */ + else request->page = wpan_phy->current_page; - } - if (info->attrs[NL802154_ATTR_SCAN_CHANNELS]) { + /* Scan all supported channels by default */ + if (info->attrs[NL802154_ATTR_SCAN_CHANNELS]) request->channels = nla_get_u32(info->attrs[NL802154_ATTR_SCAN_CHANNELS]); - if (request->channels >= BIT(IEEE802154_MAX_CHANNEL + 1)) { - pr_err("Invalid channels bitfield %x ≥ %lx\n", - request->channels, - BIT(IEEE802154_MAX_CHANNEL + 1)); - err = -EINVAL; - goto free_request; - } - } else { - /* Scan all supported channels by default */ + else request->channels = wpan_phy->supported.channels[request->page]; - } - if (info->attrs[NL802154_ATTR_SCAN_PREAMBLE_CODES] || - info->attrs[NL802154_ATTR_SCAN_MEAN_PRF]) { - pr_err("Preamble codes and mean PRF not supported yet\n"); - err = -EINVAL; - goto free_request; - } - - if (info->attrs[NL802154_ATTR_SCAN_DURATION]) { + /* Use maximum duration order by default */ + if (info->attrs[NL802154_ATTR_SCAN_DURATION]) request->duration = nla_get_u8(info->attrs[NL802154_ATTR_SCAN_DURATION]); - if (request->duration > IEEE802154_MAX_SCAN_DURATION) { - pr_err("Duration is out of range\n"); - err = -EINVAL; - goto free_request; - } - } else { - /* Use maximum duration order by default */ + else request->duration = IEEE802154_MAX_SCAN_DURATION; - } if (wpan_dev->netdev) dev_hold(wpan_dev->netdev); @@ -1614,17 +1592,11 @@ nl802154_send_beacons(struct sk_buff *skb, struct genl_info *info) request->wpan_dev = wpan_dev; request->wpan_phy = wpan_phy; - if (info->attrs[NL802154_ATTR_BEACON_INTERVAL]) { + /* Use maximum duration order by default */ + if (info->attrs[NL802154_ATTR_BEACON_INTERVAL]) request->interval = nla_get_u8(info->attrs[NL802154_ATTR_BEACON_INTERVAL]); - if (request->interval > IEEE802154_MAX_SCAN_DURATION) { - pr_err("Interval is out of range\n"); - err = -EINVAL; - goto free_request; - } - } else { - /* Use maximum duration order by default */ + else request->interval = IEEE802154_MAX_SCAN_DURATION; - } if (wpan_dev->netdev) dev_hold(wpan_dev->netdev); @@ -1640,7 +1612,7 @@ nl802154_send_beacons(struct sk_buff *skb, struct genl_info *info) free_device: if (wpan_dev->netdev) dev_put(wpan_dev->netdev); -free_request: + kfree(request); return err; From a0b6106672b53158bf141d1a713aef2c891d74b1 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 14 Feb 2023 14:50:31 +0100 Subject: [PATCH 16/20] ieee802154: Convert scan error messages to extack Instead of printing error messages in the kernel log, let's use extack. When there is a netlink error returned that could be further specified with a string, use extack as well. Apply this logic to the very recent scan/beacon infrastructure. Fixes: ed3557c947e1 ("ieee802154: Add support for user scanning requests") Fixes: 9bc114504b07 ("ieee802154: Add support for user beaconing requests") Suggested-by: Jakub Kicinski Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/r/20230214135035.1202471-3-miquel.raynal@bootlin.com Signed-off-by: Stefan Schmidt --- net/ieee802154/nl802154.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/net/ieee802154/nl802154.c b/net/ieee802154/nl802154.c index 3808a8e62a7b..12ca84e1724d 100644 --- a/net/ieee802154/nl802154.c +++ b/net/ieee802154/nl802154.c @@ -1407,9 +1407,15 @@ static int nl802154_trigger_scan(struct sk_buff *skb, struct genl_info *info) u8 type; int err; - /* Monitors are not allowed to perform scans */ - if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR) + if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR) { + NL_SET_ERR_MSG(info->extack, "Monitors are not allowed to perform scans"); return -EPERM; + } + + if (!nla_get_u8(info->attrs[NL802154_ATTR_SCAN_TYPE])) { + NL_SET_ERR_MSG(info->extack, "Malformed request, missing scan type"); + return -EINVAL; + } request = kzalloc(sizeof(*request), GFP_KERNEL); if (!request) @@ -1424,7 +1430,7 @@ static int nl802154_trigger_scan(struct sk_buff *skb, struct genl_info *info) request->type = type; break; default: - pr_err("Unsupported scan type: %d\n", type); + NL_SET_ERR_MSG_FMT(info->extack, "Unsupported scan type: %d", type); err = -EINVAL; goto free_request; } @@ -1576,12 +1582,13 @@ nl802154_send_beacons(struct sk_buff *skb, struct genl_info *info) struct cfg802154_beacon_request *request; int err; - /* Only coordinators can send beacons */ - if (wpan_dev->iftype != NL802154_IFTYPE_COORD) + if (wpan_dev->iftype != NL802154_IFTYPE_COORD) { + NL_SET_ERR_MSG(info->extack, "Only coordinators can send beacons"); return -EOPNOTSUPP; + } if (wpan_dev->pan_id == cpu_to_le16(IEEE802154_PANID_BROADCAST)) { - pr_err("Device is not part of any PAN\n"); + NL_SET_ERR_MSG(info->extack, "Device is not part of any PAN"); return -EPERM; } From 1edecbd0bd45c9c899e0f82b123342f28423468c Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 14 Feb 2023 14:50:32 +0100 Subject: [PATCH 17/20] ieee802154: Change error code on monitor scan netlink request Returning EPERM gives the impression that "right now" it is not possible, but "later" it could be, while what we want to express is the fact that this is not currently supported at all (might change in the future). So let's return EOPNOTSUPP instead. Fixes: ed3557c947e1 ("ieee802154: Add support for user scanning requests") Suggested-by: Alexander Aring Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/r/20230214135035.1202471-4-miquel.raynal@bootlin.com Signed-off-by: Stefan Schmidt --- net/ieee802154/nl802154.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ieee802154/nl802154.c b/net/ieee802154/nl802154.c index 12ca84e1724d..f4a5070a9faf 100644 --- a/net/ieee802154/nl802154.c +++ b/net/ieee802154/nl802154.c @@ -1409,7 +1409,7 @@ static int nl802154_trigger_scan(struct sk_buff *skb, struct genl_info *info) if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR) { NL_SET_ERR_MSG(info->extack, "Monitors are not allowed to perform scans"); - return -EPERM; + return -EOPNOTSUPP; } if (!nla_get_u8(info->attrs[NL802154_ATTR_SCAN_TYPE])) { From 1375e3ba9d773f2dbac96ebddfdd0d160276ca40 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 14 Feb 2023 14:50:33 +0100 Subject: [PATCH 18/20] mac802154: Send beacons using the MLME Tx path Using ieee802154_subif_start_xmit() to bypass the net queue when sending beacons is broken because it does not acquire the HARD_TX_LOCK(), hence not preventing datagram buffers to be smashed by beacons upon contention situation. Using the mlme_tx helper is not the best fit either but at least it is not buggy and has little-to-no performance hit. More details are given in the comment explaining this choice in the code. Fixes: 3accf4762734 ("mac802154: Handle basic beaconing") Reported-by: Alexander Aring Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/r/20230214135035.1202471-5-miquel.raynal@bootlin.com Signed-off-by: Stefan Schmidt --- net/mac802154/scan.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/net/mac802154/scan.c b/net/mac802154/scan.c index 8f98efec7753..fff41e59099e 100644 --- a/net/mac802154/scan.c +++ b/net/mac802154/scan.c @@ -326,7 +326,25 @@ static int mac802154_transmit_beacon(struct ieee802154_local *local, return ret; } - return ieee802154_subif_start_xmit(skb, sdata->dev); + /* 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); } void mac802154_beacon_worker(struct work_struct *work) From 61d7dddf46caa1c6dd869385a275e4d2931e7090 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 14 Feb 2023 14:50:34 +0100 Subject: [PATCH 19/20] mac802154: Fix an always true condition At this stage we simply do not care about the delayed work value, because active scan is not yet supported, so we can blindly queue another work once a beacon has been sent. It fixes a smatch warning: mac802154_beacon_worker() warn: always true condition '(local->beacon_interval >= 0) => (0-u32max >= 0)' Fixes: 3accf4762734 ("mac802154: Handle basic beaconing") Reported-by: kernel test robot Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/r/20230214135035.1202471-6-miquel.raynal@bootlin.com Signed-off-by: Stefan Schmidt --- net/mac802154/scan.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/net/mac802154/scan.c b/net/mac802154/scan.c index fff41e59099e..9b0933a185eb 100644 --- a/net/mac802154/scan.c +++ b/net/mac802154/scan.c @@ -383,9 +383,8 @@ void mac802154_beacon_worker(struct work_struct *work) 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); + queue_delayed_work(local->mac_wq, &local->beacon_work, + local->beacon_interval); } int mac802154_stop_beacons_locked(struct ieee802154_local *local, From ed9a8ad7d8a1a0eb7d4e1414d0a04ece7c2265df Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 14 Feb 2023 14:50:35 +0100 Subject: [PATCH 20/20] ieee802154: Drop device trackers In order to prevent a device from disappearing when a background job was started, dev_hold() and dev_put() calls were made. During the stabilization phase of the scan/beacon features, it was later decided that removing the device while a background job was ongoing was a valid use case, and we should instead stop the background job and then remove the device, rather than prevent the device from being removed. This is what is currently done, which means manually reference counting the device during background jobs is no longer needed. Fixes: ed3557c947e1 ("ieee802154: Add support for user scanning requests") Fixes: 9bc114504b07 ("ieee802154: Add support for user beaconing requests") Reported-by: Jakub Kicinski Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/r/20230214135035.1202471-7-miquel.raynal@bootlin.com Signed-off-by: Stefan Schmidt --- net/ieee802154/nl802154.c | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/net/ieee802154/nl802154.c b/net/ieee802154/nl802154.c index f4a5070a9faf..2215f576ee37 100644 --- a/net/ieee802154/nl802154.c +++ b/net/ieee802154/nl802154.c @@ -1453,20 +1453,14 @@ static int nl802154_trigger_scan(struct sk_buff *skb, struct genl_info *info) else request->duration = IEEE802154_MAX_SCAN_DURATION; - if (wpan_dev->netdev) - dev_hold(wpan_dev->netdev); - err = rdev_trigger_scan(rdev, request); if (err) { pr_err("Failure starting scanning (%d)\n", err); - goto free_device; + goto free_request; } return 0; -free_device: - if (wpan_dev->netdev) - dev_put(wpan_dev->netdev); free_request: kfree(request); @@ -1555,9 +1549,6 @@ int nl802154_scan_done(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev, if (err == -ESRCH) err = 0; - if (wpan_dev->netdev) - dev_put(wpan_dev->netdev); - return err; } EXPORT_SYMBOL_GPL(nl802154_scan_done); @@ -1605,21 +1596,15 @@ nl802154_send_beacons(struct sk_buff *skb, struct genl_info *info) else request->interval = IEEE802154_MAX_SCAN_DURATION; - if (wpan_dev->netdev) - dev_hold(wpan_dev->netdev); - err = rdev_send_beacons(rdev, request); if (err) { pr_err("Failure starting sending beacons (%d)\n", err); - goto free_device; + goto free_request; } return 0; -free_device: - if (wpan_dev->netdev) - dev_put(wpan_dev->netdev); - +free_request: kfree(request); return err; @@ -1627,8 +1612,7 @@ free_device: void nl802154_beaconing_done(struct wpan_dev *wpan_dev) { - if (wpan_dev->netdev) - dev_put(wpan_dev->netdev); + /* NOP */ } EXPORT_SYMBOL_GPL(nl802154_beaconing_done);