@@ -213,6 +213,17 @@ enum ieee802154_hw_flags {
* exit_scan_mode
* Exits the scan mode and returns to a fully functioning state.
* Should only be provided if ->enter_scan_mode() is populated.
+ *
+ * enter_beacons_mode
+ * Enters the beacons mode, the stack will either send beacons at a fixed
+ * rate or upon request depending on the configuration.
+ * Can be NULL, if the driver has no internal configuration to do.
+ * Returns either zero, or negative errno.
+ *
+ * exit_beacons_mode
+ * Exits the beacons mode and returns to a fully functioning state.
+ * Should only be provided if ->enter_beacons_mode() is populated.
+ * Returns either zero, or negative errno.
*/
struct ieee802154_ops {
struct module *owner;
@@ -242,6 +253,9 @@ struct ieee802154_ops {
int (*enter_scan_mode)(struct ieee802154_hw *hw,
struct cfg802154_scan_request *request);
void (*exit_scan_mode)(struct ieee802154_hw *hw);
+ int (*enter_beacons_mode)(struct ieee802154_hw *hw,
+ struct cfg802154_beacons_request *request);
+ void (*exit_beacons_mode)(struct ieee802154_hw *hw);
};
/**
@@ -311,4 +311,33 @@ static inline void drv_exit_scan_mode(struct ieee802154_local *local)
trace_802154_drv_return_void(local);
}
+static inline int drv_enter_beacons_mode(struct ieee802154_local *local,
+ struct cfg802154_beacons_request *request)
+{
+ int ret;
+
+ might_sleep();
+
+ if (!local->ops->enter_beacons_mode || !local->ops->exit_beacons_mode)
+ return 0;
+
+ trace_802154_drv_enter_beacons_mode(local, request);
+ ret = local->ops->enter_beacons_mode(&local->hw, request);
+ trace_802154_drv_return_int(local, ret);
+
+ return ret;
+}
+
+static inline void drv_exit_beacons_mode(struct ieee802154_local *local)
+{
+ might_sleep();
+
+ if (!local->ops->enter_beacons_mode || !local->ops->exit_beacons_mode)
+ return;
+
+ trace_802154_drv_exit_beacons_mode(local);
+ local->ops->exit_beacons_mode(&local->hw);
+ trace_802154_drv_return_void(local);
+}
+
#endif /* __MAC802154_DRIVER_OPS */
@@ -122,6 +122,7 @@ int mac802154_send_beacons_locked(struct ieee802154_sub_if_data *sdata,
struct cfg802154_beacons_request *request)
{
struct ieee802154_local *local = sdata->local;
+ int ret;
lockdep_assert_held(&local->beacons_lock);
@@ -130,6 +131,13 @@ int mac802154_send_beacons_locked(struct ieee802154_sub_if_data *sdata,
local->ongoing_beacons_request = true;
+ /* Either let the hardware handle the beacons or handle them manually */
+ ret = drv_enter_beacons_mode(local, request);
+ if (ret) {
+ local->ongoing_beacons_request = false;
+ return ret;
+ }
+
memset(&local->beacon, 0, sizeof(local->beacon));
local->beacon.mhr.fc.type = IEEE802154_BEACON_FRAME;
local->beacon.mhr.fc.security_enabled = 0;
@@ -179,6 +187,8 @@ int mac802154_stop_beacons_locked(struct ieee802154_local *local)
if (local->beacons_interval >= 0)
cancel_delayed_work(&local->beacons_work);
+ drv_exit_beacons_mode(local);
+
return 0;
}
@@ -292,6 +292,27 @@ DEFINE_EVENT(local_only_evt4, 802154_drv_exit_scan_mode,
TP_ARGS(local)
);
+TRACE_EVENT(802154_drv_enter_beacons_mode,
+ TP_PROTO(struct ieee802154_local *local,
+ struct cfg802154_beacons_request *request),
+ TP_ARGS(local, request),
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ __field(u8, interval)
+ ),
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ __entry->interval = request->interval;
+ ),
+ TP_printk(LOCAL_PR_FMT ", send beacons at interval: %d",
+ LOCAL_PR_ARG, __entry->interval)
+);
+
+DEFINE_EVENT(local_only_evt4, 802154_drv_exit_beacons_mode,
+ TP_PROTO(struct ieee802154_local *local),
+ TP_ARGS(local)
+);
+
#endif /* !__MAC802154_DRIVER_TRACE || TRACE_HEADER_MULTI_READ */
#undef TRACE_INCLUDE_PATH