WIPHY_PARAM_RTS_THRESHOLD = 1 << 3,
};
+/**
+ * enum tx_power_setting - TX power adjustment
+ *
+ * @TX_POWER_AUTOMATIC: the dbm parameter is ignored
+ * @TX_POWER_LIMITED: limit TX power by the dbm parameter
+ * @TX_POWER_FIXED: fix TX power to the dbm parameter
+ * @TX_POWER_OFF: turn off completely (will go away)
+ */
+enum tx_power_setting {
+ TX_POWER_AUTOMATIC,
+ TX_POWER_LIMITED,
+ TX_POWER_FIXED,
+ TX_POWER_OFF,
+};
+
/**
* struct cfg80211_ops - backend description for wireless configuration
*
* @changed bitfield (see &enum wiphy_params_flags) describes which values
* have changed. The actual parameter values are available in
* struct wiphy. If returning an error, no value should be changed.
+ *
+ * @set_tx_power: set the transmit power according to the parameters
+ * @get_tx_power: store the current TX power into the dbm variable;
+ * return 0 if successful; or -ENETDOWN if successful but power
+ * is disabled (this will go away)
*/
struct cfg80211_ops {
int (*suspend)(struct wiphy *wiphy);
int (*leave_ibss)(struct wiphy *wiphy, struct net_device *dev);
int (*set_wiphy_params)(struct wiphy *wiphy, u32 changed);
+
+ int (*set_tx_power)(struct wiphy *wiphy,
+ enum tx_power_setting type, int dbm);
+ int (*get_tx_power)(struct wiphy *wiphy, int *dbm);
};
/*
int cfg80211_wext_giwencode(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *erq, char *keybuf);
+int cfg80211_wext_siwtxpower(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *data, char *keybuf);
+int cfg80211_wext_giwtxpower(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *data, char *keybuf);
/*
* callbacks for asynchronous cfg80211 methods, notification
return 0;
}
+static int ieee80211_set_tx_power(struct wiphy *wiphy,
+ enum tx_power_setting type, int dbm)
+{
+ struct ieee80211_local *local = wiphy_priv(wiphy);
+ struct ieee80211_channel *chan = local->hw.conf.channel;
+ u32 changes = 0;
+ bool radio_enabled = true;
+
+ switch (type) {
+ case TX_POWER_AUTOMATIC:
+ local->user_power_level = -1;
+ break;
+ case TX_POWER_LIMITED:
+ if (dbm < 0)
+ return -EINVAL;
+ local->user_power_level = dbm;
+ break;
+ case TX_POWER_FIXED:
+ if (dbm < 0)
+ return -EINVAL;
+ /* TODO: move to cfg80211 when it knows the channel */
+ if (dbm > chan->max_power)
+ return -EINVAL;
+ local->user_power_level = dbm;
+ break;
+ case TX_POWER_OFF:
+ radio_enabled = false;
+ break;
+ }
+
+ if (radio_enabled != local->hw.conf.radio_enabled) {
+ changes |= IEEE80211_CONF_CHANGE_RADIO_ENABLED;
+ local->hw.conf.radio_enabled = radio_enabled;
+ }
+
+ ieee80211_hw_config(local, changes);
+
+ return 0;
+}
+
+static int ieee80211_get_tx_power(struct wiphy *wiphy, int *dbm)
+{
+ struct ieee80211_local *local = wiphy_priv(wiphy);
+
+ *dbm = local->hw.conf.power_level;
+
+ if (!local->hw.conf.radio_enabled)
+ return -ENETDOWN;
+
+ return 0;
+}
+
struct cfg80211_ops mac80211_config_ops = {
.add_virtual_intf = ieee80211_add_iface,
.del_virtual_intf = ieee80211_del_iface,
.join_ibss = ieee80211_join_ibss,
.leave_ibss = ieee80211_leave_ibss,
.set_wiphy_params = ieee80211_set_wiphy_params,
+ .set_tx_power = ieee80211_set_tx_power,
+ .get_tx_power = ieee80211_get_tx_power,
};
return 0;
}
-static int ieee80211_ioctl_siwtxpower(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *data, char *extra)
-{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_channel* chan = local->hw.conf.channel;
- bool reconf = false;
- u32 reconf_flags = 0;
- int new_power_level;
-
- if ((data->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM)
- return -EINVAL;
- if (data->txpower.flags & IW_TXPOW_RANGE)
- return -EINVAL;
- if (!chan)
- return -EINVAL;
-
- /* only change when not disabling */
- if (!data->txpower.disabled) {
- if (data->txpower.fixed) {
- if (data->txpower.value < 0)
- return -EINVAL;
- new_power_level = data->txpower.value;
- /*
- * Debatable, but we cannot do a fixed power
- * level above the regulatory constraint.
- * Use "iwconfig wlan0 txpower 15dBm" instead.
- */
- if (new_power_level > chan->max_power)
- return -EINVAL;
- } else {
- /*
- * Automatic power level setting, max being the value
- * passed in from userland.
- */
- if (data->txpower.value < 0)
- new_power_level = -1;
- else
- new_power_level = data->txpower.value;
- }
-
- reconf = true;
-
- /*
- * ieee80211_hw_config() will limit to the channel's
- * max power and possibly power constraint from AP.
- */
- local->user_power_level = new_power_level;
- }
-
- if (local->hw.conf.radio_enabled != !(data->txpower.disabled)) {
- local->hw.conf.radio_enabled = !(data->txpower.disabled);
- reconf_flags |= IEEE80211_CONF_CHANGE_RADIO_ENABLED;
- ieee80211_led_radio(local, local->hw.conf.radio_enabled);
- }
-
- if (reconf || reconf_flags)
- ieee80211_hw_config(local, reconf_flags);
-
- return 0;
-}
-
-static int ieee80211_ioctl_giwtxpower(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *data, char *extra)
-{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-
- data->txpower.fixed = 1;
- data->txpower.disabled = !(local->hw.conf.radio_enabled);
- data->txpower.value = local->hw.conf.power_level;
- data->txpower.flags = IW_TXPOW_DBM;
-
- return 0;
-}
-
static int ieee80211_ioctl_siwpower(struct net_device *dev,
struct iw_request_info *info,
struct iw_param *wrq,
(iw_handler) cfg80211_wext_giwrts, /* SIOCGIWRTS */
(iw_handler) cfg80211_wext_siwfrag, /* SIOCSIWFRAG */
(iw_handler) cfg80211_wext_giwfrag, /* SIOCGIWFRAG */
- (iw_handler) ieee80211_ioctl_siwtxpower, /* SIOCSIWTXPOW */
- (iw_handler) ieee80211_ioctl_giwtxpower, /* SIOCGIWTXPOW */
+ (iw_handler) cfg80211_wext_siwtxpower, /* SIOCSIWTXPOW */
+ (iw_handler) cfg80211_wext_giwtxpower, /* SIOCGIWTXPOW */
(iw_handler) cfg80211_wext_siwretry, /* SIOCSIWRETRY */
(iw_handler) cfg80211_wext_giwretry, /* SIOCGIWRETRY */
(iw_handler) cfg80211_wext_siwencode, /* SIOCSIWENCODE */
return err;
}
EXPORT_SYMBOL_GPL(cfg80211_wext_giwencode);
+
+int cfg80211_wext_siwtxpower(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *data, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+ enum tx_power_setting type;
+ int dbm = 0;
+
+ if ((data->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM)
+ return -EINVAL;
+ if (data->txpower.flags & IW_TXPOW_RANGE)
+ return -EINVAL;
+
+ if (!rdev->ops->set_tx_power)
+ return -EOPNOTSUPP;
+
+ /* only change when not disabling */
+ if (!data->txpower.disabled) {
+ if (data->txpower.fixed) {
+ /*
+ * wext doesn't support negative values, see
+ * below where it's for automatic
+ */
+ if (data->txpower.value < 0)
+ return -EINVAL;
+ dbm = data->txpower.value;
+ type = TX_POWER_FIXED;
+ /* TODO: do regulatory check! */
+ } else {
+ /*
+ * Automatic power level setting, max being the value
+ * passed in from userland.
+ */
+ if (data->txpower.value < 0) {
+ type = TX_POWER_AUTOMATIC;
+ } else {
+ dbm = data->txpower.value;
+ type = TX_POWER_LIMITED;
+ }
+ }
+ } else {
+ type = TX_POWER_OFF;
+ }
+
+ return rdev->ops->set_tx_power(wdev->wiphy, type, dbm);;
+}
+EXPORT_SYMBOL_GPL(cfg80211_wext_siwtxpower);
+
+int cfg80211_wext_giwtxpower(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *data, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+ int err, val;
+
+ if ((data->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM)
+ return -EINVAL;
+ if (data->txpower.flags & IW_TXPOW_RANGE)
+ return -EINVAL;
+
+ if (!rdev->ops->get_tx_power)
+ return -EOPNOTSUPP;
+
+ err = rdev->ops->get_tx_power(wdev->wiphy, &val);
+ /* HACK!!! */
+ if (err && err != -ENETDOWN)
+ return err;
+
+ /* well... oh well */
+ data->txpower.fixed = 1;
+ data->txpower.disabled = err == -ENETDOWN;
+ data->txpower.value = val;
+ data->txpower.flags = IW_TXPOW_DBM;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cfg80211_wext_giwtxpower);