+ err = mesh_path_add(dst, dev);
+ if (err) {
+ rcu_read_unlock();
+ return err;
+ }
+
+ mpath = mesh_path_lookup(dst, dev);
+ if (!mpath) {
+ rcu_read_unlock();
+ return -ENXIO;
+ }
+ mesh_path_fix_nexthop(mpath, sta);
+
+ rcu_read_unlock();
+ return 0;
+}
+
+static int ieee80211_del_mpath(struct wiphy *wiphy, struct net_device *dev,
+ u8 *dst)
+{
+ if (dst)
+ return mesh_path_del(dst, dev);
+
+ mesh_path_flush(dev);
+ return 0;
+}
+
+static int ieee80211_change_mpath(struct wiphy *wiphy,
+ struct net_device *dev,
+ u8 *dst, u8 *next_hop)
+{
+ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct mesh_path *mpath;
+ struct sta_info *sta;
+
+ if (!netif_running(dev))
+ return -ENETDOWN;
+
+ if (sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT)
+ return -ENOTSUPP;
+
+ rcu_read_lock();
+
+ sta = sta_info_get(local, next_hop);
+ if (!sta) {
+ rcu_read_unlock();
+ return -ENOENT;
+ }
+
+ mpath = mesh_path_lookup(dst, dev);
+ if (!mpath) {
+ rcu_read_unlock();
+ return -ENOENT;
+ }
+
+ mesh_path_fix_nexthop(mpath, sta);
+
+ rcu_read_unlock();
+ return 0;
+}
+
+static void mpath_set_pinfo(struct mesh_path *mpath, u8 *next_hop,
+ struct mpath_info *pinfo)
+{
+ if (mpath->next_hop)
+ memcpy(next_hop, mpath->next_hop->addr, ETH_ALEN);
+ else
+ memset(next_hop, 0, ETH_ALEN);
+
+ pinfo->filled = MPATH_INFO_FRAME_QLEN |
+ MPATH_INFO_DSN |
+ MPATH_INFO_METRIC |
+ MPATH_INFO_EXPTIME |
+ MPATH_INFO_DISCOVERY_TIMEOUT |
+ MPATH_INFO_DISCOVERY_RETRIES |
+ MPATH_INFO_FLAGS;
+
+ pinfo->frame_qlen = mpath->frame_queue.qlen;
+ pinfo->dsn = mpath->dsn;
+ pinfo->metric = mpath->metric;
+ if (time_before(jiffies, mpath->exp_time))
+ pinfo->exptime = jiffies_to_msecs(mpath->exp_time - jiffies);
+ pinfo->discovery_timeout =
+ jiffies_to_msecs(mpath->discovery_timeout);
+ pinfo->discovery_retries = mpath->discovery_retries;
+ pinfo->flags = 0;
+ if (mpath->flags & MESH_PATH_ACTIVE)
+ pinfo->flags |= NL80211_MPATH_FLAG_ACTIVE;
+ if (mpath->flags & MESH_PATH_RESOLVING)
+ pinfo->flags |= NL80211_MPATH_FLAG_RESOLVING;
+ if (mpath->flags & MESH_PATH_DSN_VALID)
+ pinfo->flags |= NL80211_MPATH_FLAG_DSN_VALID;
+ if (mpath->flags & MESH_PATH_FIXED)
+ pinfo->flags |= NL80211_MPATH_FLAG_FIXED;
+ if (mpath->flags & MESH_PATH_RESOLVING)
+ pinfo->flags |= NL80211_MPATH_FLAG_RESOLVING;
+
+ pinfo->flags = mpath->flags;
+}
+
+static int ieee80211_get_mpath(struct wiphy *wiphy, struct net_device *dev,
+ u8 *dst, u8 *next_hop, struct mpath_info *pinfo)
+
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct mesh_path *mpath;
+
+ if (sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT)
+ return -ENOTSUPP;
+
+ rcu_read_lock();
+ mpath = mesh_path_lookup(dst, dev);
+ if (!mpath) {
+ rcu_read_unlock();
+ return -ENOENT;
+ }
+ memcpy(dst, mpath->dst, ETH_ALEN);
+ mpath_set_pinfo(mpath, next_hop, pinfo);
+ rcu_read_unlock();
+ return 0;
+}
+
+static int ieee80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev,
+ int idx, u8 *dst, u8 *next_hop,
+ struct mpath_info *pinfo)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct mesh_path *mpath;
+
+ if (sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT)
+ return -ENOTSUPP;
+
+ rcu_read_lock();
+ mpath = mesh_path_lookup_by_idx(idx, dev);
+ if (!mpath) {
+ rcu_read_unlock();
+ return -ENOENT;
+ }
+ memcpy(dst, mpath->dst, ETH_ALEN);
+ mpath_set_pinfo(mpath, next_hop, pinfo);
+ rcu_read_unlock();