mirror of
https://github.com/torvalds/linux.git
synced 2024-11-17 09:31:50 +00:00
8318d78a44
This patch creates new cfg80211 wiphy API for channel and bitrate registration and converts mac80211 and drivers to the new API. The old mac80211 API is completely ripped out. All drivers (except ath5k) are updated to the new API, in many cases I expect that optimisations can be done. Along with the regulatory code I've also ripped out the IEEE80211_HW_DEFAULT_REG_DOMAIN_CONFIGURED flag, I believe it to be unnecessary if the hardware simply gives us whatever channels it wants to support and we then enable/disable them as required, which is pretty much required for travelling. Additionally, the patch adds proper "basic" rate handling for STA mode interface, AP mode interface will have to have new API added to allow userspace to set the basic rate set, currently it'll be empty... However, the basic rate handling will need to be moved to the BSS conf stuff. I do expect there to be bugs in this, especially wrt. transmit power handling where I'm basically clueless about how it should work. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
248 lines
5.7 KiB
C
248 lines
5.7 KiB
C
/*
|
|
* Copyright 2002-2005, Instant802 Networks, Inc.
|
|
* Copyright 2005-2006, Devicescape Software, Inc.
|
|
* Copyright (c) 2006 Jiri Benc <jbenc@suse.cz>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/rtnetlink.h>
|
|
#include "ieee80211_rate.h"
|
|
#include "ieee80211_i.h"
|
|
|
|
struct rate_control_alg {
|
|
struct list_head list;
|
|
struct rate_control_ops *ops;
|
|
};
|
|
|
|
static LIST_HEAD(rate_ctrl_algs);
|
|
static DEFINE_MUTEX(rate_ctrl_mutex);
|
|
|
|
static char *ieee80211_default_rc_algo = CONFIG_MAC80211_RC_DEFAULT;
|
|
module_param(ieee80211_default_rc_algo, charp, 0644);
|
|
MODULE_PARM_DESC(ieee80211_default_rc_algo,
|
|
"Default rate control algorithm for mac80211 to use");
|
|
|
|
int ieee80211_rate_control_register(struct rate_control_ops *ops)
|
|
{
|
|
struct rate_control_alg *alg;
|
|
|
|
if (!ops->name)
|
|
return -EINVAL;
|
|
|
|
mutex_lock(&rate_ctrl_mutex);
|
|
list_for_each_entry(alg, &rate_ctrl_algs, list) {
|
|
if (!strcmp(alg->ops->name, ops->name)) {
|
|
/* don't register an algorithm twice */
|
|
WARN_ON(1);
|
|
mutex_unlock(&rate_ctrl_mutex);
|
|
return -EALREADY;
|
|
}
|
|
}
|
|
|
|
alg = kzalloc(sizeof(*alg), GFP_KERNEL);
|
|
if (alg == NULL) {
|
|
mutex_unlock(&rate_ctrl_mutex);
|
|
return -ENOMEM;
|
|
}
|
|
alg->ops = ops;
|
|
|
|
list_add_tail(&alg->list, &rate_ctrl_algs);
|
|
mutex_unlock(&rate_ctrl_mutex);
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_rate_control_register);
|
|
|
|
void ieee80211_rate_control_unregister(struct rate_control_ops *ops)
|
|
{
|
|
struct rate_control_alg *alg;
|
|
|
|
mutex_lock(&rate_ctrl_mutex);
|
|
list_for_each_entry(alg, &rate_ctrl_algs, list) {
|
|
if (alg->ops == ops) {
|
|
list_del(&alg->list);
|
|
kfree(alg);
|
|
break;
|
|
}
|
|
}
|
|
mutex_unlock(&rate_ctrl_mutex);
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_rate_control_unregister);
|
|
|
|
static struct rate_control_ops *
|
|
ieee80211_try_rate_control_ops_get(const char *name)
|
|
{
|
|
struct rate_control_alg *alg;
|
|
struct rate_control_ops *ops = NULL;
|
|
|
|
if (!name)
|
|
return NULL;
|
|
|
|
mutex_lock(&rate_ctrl_mutex);
|
|
list_for_each_entry(alg, &rate_ctrl_algs, list) {
|
|
if (!strcmp(alg->ops->name, name))
|
|
if (try_module_get(alg->ops->module)) {
|
|
ops = alg->ops;
|
|
break;
|
|
}
|
|
}
|
|
mutex_unlock(&rate_ctrl_mutex);
|
|
return ops;
|
|
}
|
|
|
|
/* Get the rate control algorithm. */
|
|
static struct rate_control_ops *
|
|
ieee80211_rate_control_ops_get(const char *name)
|
|
{
|
|
struct rate_control_ops *ops;
|
|
const char *alg_name;
|
|
|
|
if (!name)
|
|
alg_name = ieee80211_default_rc_algo;
|
|
else
|
|
alg_name = name;
|
|
|
|
ops = ieee80211_try_rate_control_ops_get(alg_name);
|
|
if (!ops) {
|
|
request_module("rc80211_%s", alg_name);
|
|
ops = ieee80211_try_rate_control_ops_get(alg_name);
|
|
}
|
|
if (!ops && name)
|
|
/* try default if specific alg requested but not found */
|
|
ops = ieee80211_try_rate_control_ops_get(ieee80211_default_rc_algo);
|
|
|
|
/* try built-in one if specific alg requested but not found */
|
|
if (!ops && strlen(CONFIG_MAC80211_RC_DEFAULT))
|
|
ops = ieee80211_try_rate_control_ops_get(CONFIG_MAC80211_RC_DEFAULT);
|
|
|
|
return ops;
|
|
}
|
|
|
|
static void ieee80211_rate_control_ops_put(struct rate_control_ops *ops)
|
|
{
|
|
module_put(ops->module);
|
|
}
|
|
|
|
struct rate_control_ref *rate_control_alloc(const char *name,
|
|
struct ieee80211_local *local)
|
|
{
|
|
struct rate_control_ref *ref;
|
|
|
|
ref = kmalloc(sizeof(struct rate_control_ref), GFP_KERNEL);
|
|
if (!ref)
|
|
goto fail_ref;
|
|
kref_init(&ref->kref);
|
|
ref->ops = ieee80211_rate_control_ops_get(name);
|
|
if (!ref->ops)
|
|
goto fail_ops;
|
|
ref->priv = ref->ops->alloc(local);
|
|
if (!ref->priv)
|
|
goto fail_priv;
|
|
return ref;
|
|
|
|
fail_priv:
|
|
ieee80211_rate_control_ops_put(ref->ops);
|
|
fail_ops:
|
|
kfree(ref);
|
|
fail_ref:
|
|
return NULL;
|
|
}
|
|
|
|
static void rate_control_release(struct kref *kref)
|
|
{
|
|
struct rate_control_ref *ctrl_ref;
|
|
|
|
ctrl_ref = container_of(kref, struct rate_control_ref, kref);
|
|
ctrl_ref->ops->free(ctrl_ref->priv);
|
|
ieee80211_rate_control_ops_put(ctrl_ref->ops);
|
|
kfree(ctrl_ref);
|
|
}
|
|
|
|
void rate_control_get_rate(struct net_device *dev,
|
|
struct ieee80211_supported_band *sband,
|
|
struct sk_buff *skb,
|
|
struct rate_selection *sel)
|
|
{
|
|
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
|
|
struct rate_control_ref *ref = local->rate_ctrl;
|
|
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
|
|
struct sta_info *sta = sta_info_get(local, hdr->addr1);
|
|
int i;
|
|
|
|
memset(sel, 0, sizeof(struct rate_selection));
|
|
|
|
ref->ops->get_rate(ref->priv, dev, sband, skb, sel);
|
|
|
|
/* Select a non-ERP backup rate. */
|
|
if (!sel->nonerp) {
|
|
for (i = 0; i < sband->n_bitrates; i++) {
|
|
struct ieee80211_rate *rate = &sband->bitrates[i];
|
|
if (sel->rate->bitrate < rate->bitrate)
|
|
break;
|
|
|
|
if (rate_supported(sta, sband->band, i) &&
|
|
!(rate->flags & IEEE80211_RATE_ERP_G))
|
|
sel->nonerp = rate;
|
|
}
|
|
}
|
|
|
|
if (sta)
|
|
sta_info_put(sta);
|
|
}
|
|
|
|
struct rate_control_ref *rate_control_get(struct rate_control_ref *ref)
|
|
{
|
|
kref_get(&ref->kref);
|
|
return ref;
|
|
}
|
|
|
|
void rate_control_put(struct rate_control_ref *ref)
|
|
{
|
|
kref_put(&ref->kref, rate_control_release);
|
|
}
|
|
|
|
int ieee80211_init_rate_ctrl_alg(struct ieee80211_local *local,
|
|
const char *name)
|
|
{
|
|
struct rate_control_ref *ref, *old;
|
|
|
|
ASSERT_RTNL();
|
|
if (local->open_count || netif_running(local->mdev))
|
|
return -EBUSY;
|
|
|
|
ref = rate_control_alloc(name, local);
|
|
if (!ref) {
|
|
printk(KERN_WARNING "%s: Failed to select rate control "
|
|
"algorithm\n", wiphy_name(local->hw.wiphy));
|
|
return -ENOENT;
|
|
}
|
|
|
|
old = local->rate_ctrl;
|
|
local->rate_ctrl = ref;
|
|
if (old) {
|
|
rate_control_put(old);
|
|
sta_info_flush(local, NULL);
|
|
}
|
|
|
|
printk(KERN_DEBUG "%s: Selected rate control "
|
|
"algorithm '%s'\n", wiphy_name(local->hw.wiphy),
|
|
ref->ops->name);
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
void rate_control_deinitialize(struct ieee80211_local *local)
|
|
{
|
|
struct rate_control_ref *ref;
|
|
|
|
ref = local->rate_ctrl;
|
|
local->rate_ctrl = NULL;
|
|
rate_control_put(ref);
|
|
}
|
|
|